aboutsummaryrefslogtreecommitdiff
path: root/classes
diff options
context:
space:
mode:
Diffstat (limited to 'classes')
-rw-r--r--classes/BusinessAdmin.class.php282
-rw-r--r--classes/Parsedown.LICENSE.txt20
-rw-r--r--classes/Parsedown.class.php1421
-rw-r--r--classes/assignment.class.php316
-rw-r--r--classes/client.class.php195
-rw-r--r--classes/constants.class.php68
-rw-r--r--classes/contact.class.php378
-rw-r--r--classes/correspondence.class.php265
-rw-r--r--classes/file.class.php148
-rwxr-xr-xclasses/font/courier.php8
-rwxr-xr-xclasses/font/courierb.php8
-rwxr-xr-xclasses/font/courierbi.php8
-rwxr-xr-xclasses/font/courieri.php8
-rwxr-xr-xclasses/font/helvetica.php19
-rwxr-xr-xclasses/font/helveticab.php19
-rwxr-xr-xclasses/font/helveticabi.php19
-rwxr-xr-xclasses/font/helveticai.php19
-rwxr-xr-xclasses/font/symbol.php19
-rwxr-xr-xclasses/font/times.php19
-rwxr-xr-xclasses/font/timesb.php19
-rwxr-xr-xclasses/font/timesbi.php19
-rwxr-xr-xclasses/font/timesi.php19
-rwxr-xr-xclasses/font/zapfdingbats.php19
-rwxr-xr-xclasses/fpdf.license.txt6
-rwxr-xr-xclasses/fpdf.php1804
-rw-r--r--classes/offer.class.php628
-rw-r--r--classes/response.class.php112
27 files changed, 5865 insertions, 0 deletions
diff --git a/classes/BusinessAdmin.class.php b/classes/BusinessAdmin.class.php
new file mode 100644
index 0000000..900034f
--- /dev/null
+++ b/classes/BusinessAdmin.class.php
@@ -0,0 +1,282 @@
+<?php
+/**
+ * This file provides the BusinessAdmin class
+ *
+ * @author Camil Staps
+ *
+ * BusinessAdmin: administrative software for small companies
+ * Copyright (C) 2015 Camil Staps (ViviSoft)
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Provides basic functions like adding elements to the database
+ */
+class BusinessAdmin {
+ //------------------------------------------------------------------------------
+ // Getters and setters
+ //------------------------------------------------------------------------------
+
+ /**
+ * Get all client ids
+ *
+ * @see BusinessAdmin::getClients() This funtion returns instances of the client class instead of just the ids
+ *
+ * @param PDO $pdo The PDO class for database connection
+ *
+ * @throws PDOException Is something went wrong with the database
+ *
+ * @return int[] The ids
+ */
+ public static function getClientIds($pdo) {
+ $ids = array();
+ $clients = $pdo->query("SELECT `id` FROM `".constants::db_prefix."client`")->fetchAll(PDO::FETCH_ASSOC);
+ foreach ($clients as $client) {
+ $ids[] = $client['id'];
+ }
+ return $ids;
+ }
+
+ /**
+ * Get all clients
+ *
+ * @see BusinessAdmin::getClientIds() This function returns just the ids of the clients, and not instances of the client class
+ *
+ * @param PDO $pdo The PDO class for database connection
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return client[] An array indexed by id of instances of the client class
+ */
+ public static function getClients($pdo) {
+ $ids = self::getClientIds($pdo);
+ $clients = array();
+ foreach ($ids as $id) {
+ $clients[$id] = new client($pdo, $id);
+ }
+ return $clients;
+ }
+
+ /**
+ * Get all contact ids
+ *
+ * @see BusinessAdmin::getContacts() This funtion returns instances of the contact class instead of just the ids
+ *
+ * @param PDO $pdo The PDO class for database connection
+ *
+ * @throws PDOException Is something went wrong with the database
+ *
+ * @return int[] The ids
+ */
+ public static function getContactIds($pdo) {
+ $ids = array();
+ $contacts = $pdo->query("SELECT `id` FROM `".constants::db_prefix."contact`")->fetchAll(PDO::FETCH_ASSOC);
+ foreach ($contacts as $contact) {
+ $ids[] = $contact['id'];
+ }
+ return $ids;
+ }
+
+ /**
+ * Get all contacts
+ *
+ * @see BusinessAdmin::getContactIds() This function returns just the ids of the contacts, and not instances of the contact class
+ *
+ * @param PDO $pdo The PDO class for database connection
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return contact[] An array indexed by id of instances of the contact class
+ */
+ public static function getContacts($pdo) {
+ $ids = self::getContactIds($pdo);
+ $contacts = array();
+ foreach ($ids as $id) {
+ $contacts[$id] = new contact($pdo, $id);
+ }
+ return $contacts;
+ }
+
+ /**
+ * Get all offer ids
+ *
+ * @see BusinessAdmin::getOffers() This funtion returns instances of the offer class instead of just the ids
+ *
+ * @param PDO $pdo The PDO class for database connection
+ * @param string[] $where An array of WHERE clauses that will be AND-ed
+ *
+ * @throws PDOException Is something went wrong with the database
+ *
+ * @return int[] The ids
+ */
+ public static function getOfferIds($pdo, $where = array()) {
+ $ids = array();
+ $offers = $pdo->query("SELECT `id` FROM `".constants::db_prefix."offer`" . ((count($where) > 0) ? (" WHERE (" . implode(') AND (', $where) . ")") : ""))->fetchAll(PDO::FETCH_ASSOC);
+ foreach ($offers as $offer) {
+ $ids[] = $offer['id'];
+ }
+ return $ids;
+ }
+
+ /**
+ * Get all offers
+ *
+ * @see BusinessAdmin::getOfferIds() This function returns just the ids of the offers, and not instances of the offer class
+ *
+ * @param PDO $pdo The PDO class for database connection
+ * @param string[] $where An array of WHERE clauses that will be AND-ed
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return offer[] An array indexed by id of instances of the offer class
+ */
+ public static function getOffers($pdo, $where = array()) {
+ $ids = self::getOfferIds($pdo, $where);
+ $offers = array();
+ foreach ($ids as $id) {
+ $offers[$id] = new offer($pdo, $id);
+ }
+ return $offers;
+ }
+
+ /**
+ * Get all assignment ids
+ *
+ * @see BusinessAdmin::getAssignments() This funtion returns instances of the assignment class instead of just the ids
+ *
+ * @param PDO $pdo The PDO class for database connection
+ *
+ * @throws PDOException Is something went wrong with the database
+ *
+ * @return int[] The ids
+ */
+ public static function getAssignmentIds($pdo) {
+ $ids = array();
+ $assignments = $pdo->query("SELECT `id` FROM `".constants::db_prefix."assignment`")->fetchAll(PDO::FETCH_ASSOC);
+ foreach ($assignments as $assignment) {
+ $ids[] = $assignment['id'];
+ }
+ return $ids;
+ }
+
+ /**
+ * Get all assignments
+ *
+ * @see BusinessAdmin::getAssignmentIds() This function returns just the ids of the assignments, and not instances of the assignment class
+ *
+ * @param PDO $pdo The PDO class for database connection
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return assignment[] An array indexed by id of instances of the assignment class
+ */
+ public static function getAssignments($pdo) {
+ $ids = self::getAssignmentIds($pdo);
+ $assignments = array();
+ foreach ($ids as $id) {
+ $assignments[$id] = new assignment($pdo, $id);
+ }
+ return $assignments;
+ }
+
+ //------------------------------------------------------------------------------
+ // Other functions
+ //------------------------------------------------------------------------------
+
+ /**
+ * Create a new client
+ *
+ * @param PDO $pdo The database connection
+ * @param string $name The name for the new client
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return client|bool A new instance of the client object, or false on failure
+ */
+ public static function createClient($pdo, $name) {
+ $stmt = $pdo->prepare("INSERT INTO `".constants::db_prefix."client` (`name`) VALUES (?)");
+ $stmt->execute(array($name));
+ if ($stmt->rowCount() == 1) {
+ return new client($pdo, $pdo->lastInsertId());
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Create a new file
+ *
+ * @param PDO $pdo The database connection
+ * @param string $filename The desired filename
+ *
+ * @throws PDOException If something went wrong with the database
+ * @throws Exception If the file could not be created (due to permissions, file existed already, etc.), or the database record couldn't be added
+ *
+ * @return file A new instance of the file object
+ */
+ public static function createFile($pdo, $filename) {
+ // Check for file existence
+ if (file_exists(constants::files_folder . $filename)) {
+ throw new Exception("$filename already exists.");
+ }
+
+ // Try to create the file
+ if (file_put_contents(constants::files_folder . $filename, '') === false) {
+ throw new Exception("$filename could not be created. Check the permissions.");
+ }
+
+ $stmt = $pdo->prepare("INSERT INTO `".constants::db_prefix."file` (`filename`) VALUES (?)");
+ $stmt->execute(array($filename));
+ if ($stmt->rowCount() == 1) {
+ return new file($pdo, $pdo->lastInsertId());
+ } else {
+ unlink(constants::files_folder . $filename);
+ throw new Exception("$filename could not be added to the database");
+ }
+ }
+
+ /**
+ * Format a date nicely
+ *
+ * @todo implement $relatively = true
+ *
+ * @param int $timestamp The UNIX timestamp to format
+ * @param bool $with_time If false, only the date is returned; if false, also the time
+ * @param bool $full_date If true, a year will be outputted even if the date is in the current year
+ * @param bool $relatively Whether or not to show the date relatively (e.g. '1 day ago') (NOT IMPLEMENTED YET)
+ *
+ * @return string The formatted date
+ */
+ public static function formatDate($timestamp, $with_time = true, $full_date = false, $relatively = false) {
+ $output = '';
+ if (date('Y', $timestamp) == 1970) {
+ return 'never';
+ }
+ if (date('d/m/Y') == date('d/m/Y', $timestamp)) {
+ return 'today';
+ }
+ if (date('Y') != date('Y', $timestamp) || $full_date) {
+ $output .= date('Y-', $timestamp);
+ }
+ if (date('d/m/Y') != date('d/m/Y', $timestamp) || $full_date) {
+ $output .= date('m-d', $timestamp);
+ }
+ if ($with_time) {
+ $output .= date(' H:i', $timestamp);
+ }
+
+ return $output;
+ }
+} \ No newline at end of file
diff --git a/classes/Parsedown.LICENSE.txt b/classes/Parsedown.LICENSE.txt
new file mode 100644
index 0000000..baca86f
--- /dev/null
+++ b/classes/Parsedown.LICENSE.txt
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Emanuil Rusev, erusev.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
diff --git a/classes/Parsedown.class.php b/classes/Parsedown.class.php
new file mode 100644
index 0000000..4a85820
--- /dev/null
+++ b/classes/Parsedown.class.php
@@ -0,0 +1,1421 @@
+<?php
+
+#
+#
+# Parsedown
+# http://parsedown.org
+#
+# (c) Emanuil Rusev
+# http://erusev.com
+#
+# For the full license information, view the LICENSE file that was distributed
+# with this source code.
+#
+#
+
+class Parsedown
+{
+ #
+ # Philosophy
+
+ # Parsedown recognises that the Markdown syntax is optimised for humans so
+ # it tries to read like one. It goes through text line by line. It looks at
+ # how lines start to identify blocks. It looks for special characters to
+ # identify inline elements.
+
+ #
+ # ~
+
+ function text($text)
+ {
+ # make sure no definitions are set
+ $this->Definitions = array();
+
+ # standardize line breaks
+ $text = str_replace("\r\n", "\n", $text);
+ $text = str_replace("\r", "\n", $text);
+
+ # replace tabs with spaces
+ $text = str_replace("\t", ' ', $text);
+
+ # remove surrounding line breaks
+ $text = trim($text, "\n");
+
+ # split text into lines
+ $lines = explode("\n", $text);
+
+ # iterate through lines to identify blocks
+ $markup = $this->lines($lines);
+
+ # trim line breaks
+ $markup = trim($markup, "\n");
+
+ return $markup;
+ }
+
+ #
+ # Setters
+ #
+
+ private $breaksEnabled;
+
+ function setBreaksEnabled($breaksEnabled)
+ {
+ $this->breaksEnabled = $breaksEnabled;
+
+ return $this;
+ }
+
+ private $markupEscaped;
+
+ function setMarkupEscaped($markupEscaped)
+ {
+ $this->markupEscaped = $markupEscaped;
+
+ return $this;
+ }
+
+ #
+ # Lines
+ #
+
+ protected $BlockTypes = array(
+ '#' => array('Atx'),
+ '*' => array('Rule', 'List'),
+ '+' => array('List'),
+ '-' => array('Setext', 'Table', 'Rule', 'List'),
+ '0' => array('List'),
+ '1' => array('List'),
+ '2' => array('List'),
+ '3' => array('List'),
+ '4' => array('List'),
+ '5' => array('List'),
+ '6' => array('List'),
+ '7' => array('List'),
+ '8' => array('List'),
+ '9' => array('List'),
+ ':' => array('Table'),
+ '<' => array('Comment', 'Markup'),
+ '=' => array('Setext'),
+ '>' => array('Quote'),
+ '_' => array('Rule'),
+ '`' => array('FencedCode'),
+ '|' => array('Table'),
+ '~' => array('FencedCode'),
+ );
+
+ # ~
+
+ protected $DefinitionTypes = array(
+ '[' => array('Reference'),
+ );
+
+ # ~
+
+ protected $unmarkedBlockTypes = array(
+ 'CodeBlock',
+ );
+
+ #
+ # Blocks
+ #
+
+ private function lines(array $lines)
+ {
+ $CurrentBlock = null;
+
+ foreach ($lines as $line)
+ {
+ if (chop($line) === '')
+ {
+ if (isset($CurrentBlock))
+ {
+ $CurrentBlock['interrupted'] = true;
+ }
+
+ continue;
+ }
+
+ $indent = 0;
+
+ while (isset($line[$indent]) and $line[$indent] === ' ')
+ {
+ $indent ++;
+ }
+
+ $text = $indent > 0 ? substr($line, $indent) : $line;
+
+ # ~
+
+ $Line = array('body' => $line, 'indent' => $indent, 'text' => $text);
+
+ # ~
+
+ if (isset($CurrentBlock['incomplete']))
+ {
+ $Block = $this->{'addTo'.$CurrentBlock['type']}($Line, $CurrentBlock);
+
+ if (isset($Block))
+ {
+ $CurrentBlock = $Block;
+
+ continue;
+ }
+ else
+ {
+ if (method_exists($this, 'complete'.$CurrentBlock['type']))
+ {
+ $CurrentBlock = $this->{'complete'.$CurrentBlock['type']}($CurrentBlock);
+ }
+
+ unset($CurrentBlock['incomplete']);
+ }
+ }
+
+ # ~
+
+ $marker = $text[0];
+
+ if (isset($this->DefinitionTypes[$marker]))
+ {
+ foreach ($this->DefinitionTypes[$marker] as $definitionType)
+ {
+ $Definition = $this->{'identify'.$definitionType}($Line, $CurrentBlock);
+
+ if (isset($Definition))
+ {
+ $this->Definitions[$definitionType][$Definition['id']] = $Definition['data'];
+
+ continue 2;
+ }
+ }
+ }
+
+ # ~
+
+ $blockTypes = $this->unmarkedBlockTypes;
+
+ if (isset($this->BlockTypes[$marker]))
+ {
+ foreach ($this->BlockTypes[$marker] as $blockType)
+ {
+ $blockTypes []= $blockType;
+ }
+ }
+
+ #
+ # ~
+
+ foreach ($blockTypes as $blockType)
+ {
+ $Block = $this->{'identify'.$blockType}($Line, $CurrentBlock);
+
+ if (isset($Block))
+ {
+ $Block['type'] = $blockType;
+
+ if ( ! isset($Block['identified']))
+ {
+ $Elements []= $CurrentBlock['element'];
+
+ $Block['identified'] = true;
+ }
+
+ if (method_exists($this, 'addTo'.$blockType))
+ {
+ $Block['incomplete'] = true;
+ }
+
+ $CurrentBlock = $Block;
+
+ continue 2;
+ }
+ }
+
+ # ~
+
+ if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted']))
+ {
+ $CurrentBlock['element']['text'] .= "\n".$text;
+ }
+ else
+ {
+ $Elements []= $CurrentBlock['element'];
+
+ $CurrentBlock = $this->buildParagraph($Line);
+
+ $CurrentBlock['identified'] = true;
+ }
+ }
+
+ # ~
+
+ if (isset($CurrentBlock['incomplete']) and method_exists($this, 'complete'.$CurrentBlock['type']))
+ {
+ $CurrentBlock = $this->{'complete'.$CurrentBlock['type']}($CurrentBlock);
+ }
+
+ # ~
+
+ $Elements []= $CurrentBlock['element'];
+
+ unset($Elements[0]);
+
+ # ~
+
+ $markup = $this->elements($Elements);
+
+ # ~
+
+ return $markup;
+ }
+
+ #
+ # Atx
+
+ protected function identifyAtx($Line)
+ {
+ if (isset($Line['text'][1]))
+ {
+ $level = 1;
+
+ while (isset($Line['text'][$level]) and $Line['text'][$level] === '#')
+ {
+ $level ++;
+ }
+
+ $text = trim($Line['text'], '# ');
+
+ $Block = array(
+ 'element' => array(
+ 'name' => 'h' . min(6, $level),
+ 'text' => $text,
+ 'handler' => 'line',
+ ),
+ );
+
+ return $Block;
+ }
+ }
+
+ #
+ # Code
+
+ protected function identifyCodeBlock($Line)
+ {
+ if ($Line['indent'] >= 4)
+ {
+ $text = substr($Line['body'], 4);
+
+ $Block = array(
+ 'element' => array(
+ 'name' => 'pre',
+ 'handler' => 'element',
+ 'text' => array(
+ 'name' => 'code',
+ 'text' => $text,
+ ),
+ ),
+ );
+
+ return $Block;
+ }
+ }
+
+ protected function addToCodeBlock($Line, $Block)
+ {
+ if ($Line['indent'] >= 4)
+ {
+ if (isset($Block['interrupted']))
+ {
+ $Block['element']['text']['text'] .= "\n";
+
+ unset($Block['interrupted']);
+ }
+
+ $Block['element']['text']['text'] .= "\n";
+
+ $text = substr($Line['body'], 4);
+
+ $Block['element']['text']['text'] .= $text;
+
+ return $Block;
+ }
+ }
+
+ protected function completeCodeBlock($Block)
+ {
+ $text = $Block['element']['text']['text'];
+
+ $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
+
+ $Block['element']['text']['text'] = $text;
+
+ return $Block;
+ }
+
+ #
+ # Comment
+
+ protected function identifyComment($Line)
+ {
+ if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!')
+ {
+ $Block = array(
+ 'element' => $Line['body'],
+ );
+
+ if (preg_match('/-->$/', $Line['text']))
+ {
+ $Block['closed'] = true;
+ }
+
+ return $Block;
+ }
+ }
+
+ protected function addToComment($Line, array $Block)
+ {
+ if (isset($Block['closed']))
+ {
+ return;
+ }
+
+ $Block['element'] .= "\n" . $Line['body'];
+
+ if (preg_match('/-->$/', $Line['text']))
+ {
+ $Block['closed'] = true;
+ }
+
+ return $Block;
+ }
+
+ #
+ # Fenced Code
+
+ protected function identifyFencedCode($Line)
+ {
+ if (preg_match('/^(['.$Line['text'][0].']{3,})[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches))
+ {
+ $Element = array(
+ 'name' => 'code',
+ 'text' => '',
+ );
+
+ if (isset($matches[2]))
+ {
+ $class = 'language-'.$matches[2];
+
+ $Element['attributes'] = array(
+ 'class' => $class,
+ );
+ }
+
+ $Block = array(
+ 'char' => $Line['text'][0],
+ 'element' => array(
+ 'name' => 'pre',
+ 'handler' => 'element',
+ 'text' => $Element,
+ ),
+ );
+
+ return $Block;
+ }
+ }
+
+ protected function addToFencedCode($Line, $Block)
+ {
+ if (isset($Block['complete']))
+ {
+ return;
+ }
+
+ if (isset($Block['interrupted']))
+ {
+ $Block['element']['text']['text'] .= "\n";
+
+ unset($Block['interrupted']);
+ }
+
+ if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text']))
+ {
+ $Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1);
+
+ $Block['complete'] = true;
+
+ return $Block;
+ }
+
+ $Block['element']['text']['text'] .= "\n".$Line['body'];;
+
+ return $Block;
+ }
+
+ protected function completeFencedCode($Block)
+ {
+ $text = $Block['element']['text']['text'];
+
+ $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
+
+ $Block['element']['text']['text'] = $text;
+
+ return $Block;
+ }
+
+ #
+ # List
+
+ protected function identifyList($Line)
+ {
+ list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]');
+
+ if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches))
+ {
+ $Block = array(
+ 'indent' => $Line['indent'],
+ 'pattern' => $pattern,
+ 'element' => array(
+ 'name' => $name,
+ 'handler' => 'elements',
+ ),
+ );
+
+ $Block['li'] = array(
+ 'name' => 'li',
+ 'handler' => 'li',
+ 'text' => array(
+ $matches[2],
+ ),
+ );
+
+ $Block['element']['text'] []= & $Block['li'];
+
+ return $Block;
+ }
+ }
+
+ protected function addToList($Line, array $Block)
+ {
+ if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'[ ]+(.*)/', $Line['text'], $matches))
+ {
+ if (isset($Block['interrupted']))
+ {
+ $Block['li']['text'] []= '';
+
+ unset($Block['interrupted']);
+ }
+
+ unset($Block['li']);
+
+ $Block['li'] = array(
+ 'name' => 'li',
+ 'handler' => 'li',
+ 'text' => array(
+ $matches[1],
+ ),
+ );
+
+ $Block['element']['text'] []= & $Block['li'];
+
+ return $Block;
+ }
+
+ if ( ! isset($Block['interrupted']))
+ {
+ $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
+
+ $Block['li']['text'] []= $text;
+
+ return $Block;
+ }
+
+ if ($Line['indent'] > 0)
+ {
+ $Block['li']['text'] []= '';
+
+ $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
+
+ $Block['li']['text'] []= $text;
+
+ unset($Block['interrupted']);
+
+ return $Block;
+ }
+ }
+
+ #
+ # Quote
+
+ protected function identifyQuote($Line)
+ {
+ if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
+ {
+ $Block = array(
+ 'element' => array(
+ 'name' => 'blockquote',
+ 'handler' => 'lines',
+ 'text' => (array) $matches[1],
+ ),
+ );
+
+ return $Block;
+ }
+ }
+
+ protected function addToQuote($Line, array $Block)
+ {
+ if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
+ {
+ if (isset($Block['interrupted']))
+ {
+ $Block['element']['text'] []= '';
+
+ unset($Block['interrupted']);
+ }
+
+ $Block['element']['text'] []= $matches[1];
+
+ return $Block;
+ }
+
+ if ( ! isset($Block['interrupted']))
+ {
+ $Block['element']['text'] []= $Line['text'];
+
+ return $Block;
+ }
+ }
+
+ #
+ # Rule
+
+ protected function identifyRule($Line)
+ {
+ if (preg_match('/^(['.$Line['text'][0].'])([ ]{0,2}\1){2,}[ ]*$/', $Line['text']))
+ {
+ $Block = array(
+ 'element' => array(
+ 'name' => 'hr'
+ ),
+ );
+
+ return $Block;
+ }
+ }
+
+ #
+ # Setext
+
+ protected function identifySetext($Line, array $Block = null)
+ {
+ if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
+ {
+ return;
+ }
+
+ if (chop($Line['text'], $Line['text'][0]) === '')
+ {
+ $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';
+
+ return $Block;
+ }
+ }
+
+ #
+ # Markup
+
+ protected function identifyMarkup($Line)
+ {
+ if ($this->markupEscaped)
+ {
+ return;
+ }
+
+ if (preg_match('/^<(\w[\w\d]*)(?:[ ][^>]*)?(\/?)[ ]*>/', $Line['text'], $matches))
+ {
+ if (in_array($matches[1], $this->textLevelElements))
+ {
+ return;
+ }
+
+ $Block = array(
+ 'element' => $Line['body'],
+ );
+
+ if ($matches[2] or $matches[1] === 'hr' or preg_match('/<\/'.$matches[1].'>[ ]*$/', $Line['text']))
+ {
+ $Block['closed'] = true;
+ }
+ else
+ {
+ $Block['depth'] = 0;
+ $Block['name'] = $matches[1];
+ }
+
+ return $Block;
+ }
+ }
+
+ protected function addToMarkup($Line, array $Block)
+ {
+ if (isset($Block['closed']))
+ {
+ return;
+ }
+
+ if (preg_match('/<'.$Block['name'].'([ ][^\/]+)?>/', $Line['text'])) # opening tag
+ {
+ $Block['depth'] ++;
+ }
+
+ if (stripos($Line['text'], '</'.$Block['name'].'>') !== false) # closing tag
+ {
+ if ($Block['depth'] > 0)
+ {
+ $Block['depth'] --;
+ }
+ else
+ {
+ $Block['closed'] = true;
+ }
+ }
+
+ $Block['element'] .= "\n".$Line['body'];
+
+ return $Block;
+ }
+
+ #
+ # Table
+
+ protected function identifyTable($Line, array $Block = null)
+ {
+ if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
+ {
+ return;
+ }
+
+ if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '')
+ {
+ $alignments = array();
+
+ $divider = $Line['text'];
+
+ $divider = trim($divider);
+ $divider = trim($divider, '|');
+
+ $dividerCells = explode('|', $divider);
+
+ foreach ($dividerCells as $dividerCell)
+ {
+ $dividerCell = trim($dividerCell);
+
+ if ($dividerCell === '')
+ {
+ continue;
+ }
+
+ $alignment = null;
+
+ if ($dividerCell[0] === ':')
+ {
+ $alignment = 'left';
+ }
+
+ if (substr($dividerCell, -1) === ':')
+ {
+ $alignment = $alignment === 'left' ? 'center' : 'right';
+ }
+
+ $alignments []= $alignment;
+ }
+
+ # ~
+
+ $HeaderElements = array();
+
+ $header = $Block['element']['text'];
+
+ $header = trim($header);
+ $header = trim($header, '|');
+
+ $headerCells = explode('|', $header);
+
+ foreach ($headerCells as $index => $headerCell)
+ {
+ $headerCell = trim($headerCell);
+
+ $HeaderElement = array(
+ 'name' => 'th',
+ 'text' => $headerCell,
+ 'handler' => 'line',
+ );
+
+ if (isset($alignments[$index]))
+ {
+ $alignment = $alignments[$index];
+
+ $HeaderElement['attributes'] = array(
+ 'align' => $alignment,
+ );
+ }
+
+ $HeaderElements []= $HeaderElement;
+ }
+
+ # ~
+
+ $Block = array(
+ 'alignments' => $alignments,
+ 'identified' => true,
+ 'element' => array(
+ 'name' => 'table',
+ 'handler' => 'elements',
+ ),
+ );
+
+ $Block['element']['text'] []= array(
+ 'name' => 'thead',
+ 'handler' => 'elements',
+ );
+
+ $Block['element']['text'] []= array(
+ 'name' => 'tbody',
+ 'handler' => 'elements',
+ 'text' => array(),
+ );
+
+ $Block['element']['text'][0]['text'] []= array(
+ 'name' => 'tr',
+ 'handler' => 'elements',
+ 'text' => $HeaderElements,
+ );
+
+ return $Block;
+ }
+ }
+
+ protected function addToTable($Line, array $Block)
+ {
+ if ($Line['text'][0] === '|' or strpos($Line['text'], '|'))
+ {
+ $Elements = array();
+
+ $row = $Line['text'];
+
+ $row = trim($row);
+ $row = trim($row, '|');
+
+ $cells = explode('|', $row);
+
+ foreach ($cells as $index => $cell)
+ {
+ $cell = trim($cell);
+
+ $Element = array(
+ 'name' => 'td',
+ 'handler' => 'line',
+ 'text' => $cell,
+ );
+
+ if (isset($Block['alignments'][$index]))
+ {
+ $Element['attributes'] = array(
+ 'align' => $Block['alignments'][$index],
+ );
+ }
+
+ $Elements []= $Element;
+ }
+
+ $Element = array(
+ 'name' => 'tr',
+ 'handler' => 'elements',
+ 'text' => $Elements,
+ );
+
+ $Block['element']['text'][1]['text'] []= $Element;
+
+ return $Block;
+ }
+ }
+
+ #
+ # Definitions
+ #
+
+ protected function identifyReference($Line)
+ {
+ if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
+ {
+ $Definition = array(
+ 'id' => strtolower($matches[1]),
+ 'data' => array(
+ 'url' => $matches[2],
+ ),
+ );
+
+ if (isset($matches[3]))
+ {
+ $Definition['data']['title'] = $matches[3];
+ }
+
+ return $Definition;
+ }
+ }
+
+ #
+ # ~
+ #
+
+ protected function buildParagraph($Line)
+ {
+ $Block = array(
+ 'element' => array(
+ 'name' => 'p',
+ 'text' => $Line['text'],
+ 'handler' => 'line',
+ ),
+ );
+
+ return $Block;
+ }
+
+ #
+ # ~
+ #
+
+ protected function element(array $Element)
+ {
+ $markup = '<'.$Element['name'];
+
+ if (isset($Element['attributes']))
+ {
+ foreach ($Element['attributes'] as $name => $value)
+ {
+ $markup .= ' '.$name.'="'.$value.'"';
+ }
+ }
+
+ if (isset($Element['text']))
+ {
+ $markup .= '>';
+
+ if (isset($Element['handler']))
+ {
+ $markup .= $this->$Element['handler']($Element['text']);
+ }
+ else
+ {
+ $markup .= $Element['text'];
+ }
+
+ $markup .= '</'.$Element['name'].'>';
+ }
+ else
+ {
+ $markup .= ' />';
+ }
+
+ return $markup;
+ }
+
+ protected function elements(array $Elements)
+ {
+ $markup = '';
+
+ foreach ($Elements as $Element)
+ {
+ if ($Element === null)
+ {
+ continue;
+ }
+
+ $markup .= "\n";
+
+ if (is_string($Element)) # because of Markup
+ {
+ $markup .= $Element;
+
+ continue;
+ }
+
+ $markup .= $this->element($Element);
+ }
+
+ $markup .= "\n";
+
+ return $markup;
+ }
+
+ #
+ # Spans
+ #
+
+ protected $SpanTypes = array(
+ '!' => array('Link'), # ?
+ '&' => array('Ampersand'),
+ '*' => array('Emphasis'),
+ '/' => array('Url'),
+ '<' => array('UrlTag', 'EmailTag', 'Tag', 'LessThan'),
+ '[' => array('Link'),
+ '_' => array('Emphasis'),
+ '`' => array('InlineCode'),
+ '~' => array('Strikethrough'),
+ '\\' => array('EscapeSequence'),
+ );
+
+ # ~
+
+ protected $spanMarkerList = '*_!&[</`~\\';
+
+ #
+ # ~
+ #
+
+ public function line($text)
+ {
+ $markup = '';
+
+ $remainder = $text;
+
+ $markerPosition = 0;
+
+ while ($excerpt = strpbrk($remainder, $this->spanMarkerList))
+ {
+ $marker = $excerpt[0];
+
+ $markerPosition += strpos($remainder, $marker);
+
+ $Excerpt = array('text' => $excerpt, 'context' => $text);
+
+ foreach ($this->SpanTypes[$marker] as $spanType)
+ {
+ $handler = 'identify'.$spanType;
+
+ $Span = $this->$handler($Excerpt);
+
+ if ( ! isset($Span))
+ {
+ continue;
+ }
+
+ # The identified span can be ahead of the marker.
+
+ if (isset($Span['position']) and $Span['position'] > $markerPosition)
+ {
+ continue;
+ }
+
+ # Spans that start at the position of their marker don't have to set a position.
+
+ if ( ! isset($Span['position']))
+ {
+ $Span['position'] = $markerPosition;
+ }
+
+ $plainText = substr($text, 0, $Span['position']);
+
+ $markup .= $this->readPlainText($plainText);
+
+ $markup .= isset($Span['markup']) ? $Span['markup'] : $this->element($Span['element']);
+
+ $text = substr($text, $Span['position'] + $Span['extent']);
+
+ $remainder = $text;
+
+ $markerPosition = 0;
+
+ continue 2;
+ }
+
+ $remainder = substr($excerpt, 1);
+
+ $markerPosition ++;
+ }
+
+ $markup .= $this->readPlainText($text);
+
+ return $markup;
+ }
+
+ #
+ # ~
+ #
+
+ protected function identifyUrl($Excerpt)
+ {
+ if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '/')
+ {
+ return;
+ }
+
+ if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
+ {
+ $url = str_replace(array('&', '<'), array('&amp;', '&lt;'), $matches[0][0]);
+
+ return array(
+ 'extent' => strlen($matches[0][0]),
+ 'position' => $matches[0][1],
+ 'element' => array(
+ 'name' => 'a',
+ 'text' => $url,
+ 'attributes' => array(
+ 'href' => $url,
+ ),
+ ),
+ );
+ }
+ }
+
+ protected function identifyAmpersand($Excerpt)
+ {
+ if ( ! preg_match('/^&#?\w+;/', $Excerpt['text']))
+ {
+ return array(
+ 'markup' => '&amp;',
+ 'extent' => 1,
+ );
+ }
+ }
+
+ protected function identifyStrikethrough($Excerpt)
+ {
+ if ( ! isset($Excerpt['text'][1]))
+ {
+ return;
+ }
+
+ if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches))
+ {
+ return array(
+ 'extent' => strlen($matches[0]),
+ 'element' => array(
+ 'name' => 'del',
+ 'text' => $matches[1],
+ 'handler' => 'line',
+ ),
+ );
+ }
+ }
+
+ protected function identifyEscapeSequence($Excerpt)
+ {
+ if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
+ {
+ return array(
+ 'markup' => $Excerpt['text'][1],
+ 'extent' => 2,
+ );
+ }
+ }
+
+ protected function identifyLessThan()
+ {
+ return array(
+ 'markup' => '&lt;',
+ 'extent' => 1,
+ );
+ }
+
+ protected function identifyUrlTag($Excerpt)
+ {
+ if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(https?:[\/]{2}[^\s]+?)>/i', $Excerpt['text'], $matches))
+ {
+ $url = str_replace(array('&', '<'), array('&amp;', '&lt;'), $matches[1]);
+
+ return array(
+ 'extent' => strlen($matches[0]),
+ 'element' => array(
+ 'name' => 'a',
+ 'text' => $url,
+ 'attributes' => array(
+ 'href' => $url,
+ ),
+ ),
+ );
+ }
+ }
+
+ protected function identifyEmailTag($Excerpt)
+ {
+ if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\S+?@\S+?)>/', $Excerpt['text'], $matches))
+ {
+ return array(
+ 'extent' => strlen($matches[0]),
+ 'element' => array(
+ 'name' => 'a',
+ 'text' => $matches[1],
+ 'attributes' => array(
+ 'href' => 'mailto:'.$matches[1],
+ ),
+ ),
+ );
+ }
+ }
+
+ protected function identifyTag($Excerpt)
+ {
+ if ($this->markupEscaped)
+ {
+ return;
+ }
+
+ if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<\/?\w.*?>/', $Excerpt['text'], $matches))
+ {
+ return array(
+ 'markup' => $matches[0],
+ 'extent' => strlen($matches[0]),
+ );
+ }
+ }
+
+ protected function identifyInlineCode($Excerpt)
+ {
+ $marker = $Excerpt['text'][0];
+
+ if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/', $Excerpt['text'], $matches))
+ {
+ $text = $matches[2];
+ $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
+
+ return array(
+ 'extent' => strlen($matches[0]),
+ 'element' => array(
+ 'name' => 'code',
+ 'text' => $text,
+ ),
+ );
+ }
+ }
+
+ protected function identifyLink($Excerpt)
+ {
+ $extent = $Excerpt['text'][0] === '!' ? 1 : 0;
+
+ if (strpos($Excerpt['text'], ']') and preg_match('/\[((?:[^][]|(?R))*)\]/', $Excerpt['text'], $matches))
+ {
+ $Link = array('text' => $matches[1], 'label' => strtolower($matches[1]));
+
+ $extent += strlen($matches[0]);
+
+ $substring = substr($Excerpt['text'], $extent);
+
+ if (preg_match('/^\s*\[([^][]+)\]/', $substring, $matches))
+ {
+ $Link['label'] = strtolower($matches[1]);
+
+ if (isset($this->Definitions['Reference'][$Link['label']]))
+ {
+ $Link += $this->Definitions['Reference'][$Link['label']];
+
+ $extent += strlen($matches[0]);
+ }
+ else
+ {
+ return;
+ }
+ }
+ elseif (isset($this->Definitions['Reference'][$Link['label']]))
+ {
+ $Link += $this->Definitions['Reference'][$Link['label']];
+
+ if (preg_match('/^[ ]*\[\]/', $substring, $matches))
+ {
+ $extent += strlen($matches[0]);
+ }
+ }
+ elseif (preg_match('/^\([ ]*(.*?)(?:[ ]+[\'"](.+?)[\'"])?[ ]*\)/', $substring, $matches))
+ {
+ $Link['url'] = $matches[1];
+
+ if (isset($matches[2]))
+ {
+ $Link['title'] = $matches[2];
+ }
+
+ $extent += strlen($matches[0]);
+ }
+ else
+ {
+ return;
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ $url = str_replace(array('&', '<'), array('&amp;', '&lt;'), $Link['url']);
+
+ if ($Excerpt['text'][0] === '!')
+ {
+ $Element = array(
+ 'name' => 'img',
+ 'attributes' => array(
+ 'alt' => $Link['text'],
+ 'src' => $url,
+ ),
+ );
+ }
+ else
+ {
+ $Element = array(
+ 'name' => 'a',
+ 'handler' => 'line',
+ 'text' => $Link['text'],
+ 'attributes' => array(
+ 'href' => $url,
+ ),
+ );
+ }
+
+ if (isset($Link['title']))
+ {
+ $Element['attributes']['title'] = $Link['title'];
+ }
+
+ return array(
+ 'extent' => $extent,
+ 'element' => $Element,
+ );
+ }
+
+ protected function identifyEmphasis($Excerpt)
+ {
+ if ( ! isset($Excerpt['text'][1]))
+ {
+ return;
+ }
+
+ $marker = $Excerpt['text'][0];
+
+ if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
+ {
+ $emphasis = 'strong';
+ }
+ elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
+ {
+ $emphasis = 'em';
+ }
+ else
+ {
+ return;
+ }
+
+ return array(
+ 'extent' => strlen($matches[0]),
+ 'element' => array(
+ 'name' => $emphasis,
+ 'handler' => 'line',
+ 'text' => $matches[1],
+ ),
+ );
+ }
+
+ #
+ # ~
+
+ protected function readPlainText($text)
+ {
+ $breakMarker = $this->breaksEnabled ? "\n" : " \n";
+
+ $text = str_replace($breakMarker, "<br />\n", $text);
+
+ return $text;
+ }
+
+ #
+ # ~
+ #
+
+ protected function li($lines)
+ {
+ $markup = $this->lines($lines);
+
+ $trimmedMarkup = trim($markup);
+
+ if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '<p>')
+ {
+ $markup = $trimmedMarkup;
+ $markup = substr($markup, 3);
+
+ $position = strpos($markup, "</p>");
+
+ $markup = substr_replace($markup, '', $position, 4);
+ }
+
+ return $markup;
+ }
+
+ #
+ # Multiton
+ #
+
+ static function instance($name = 'default')
+ {
+ if (isset(self::$instances[$name]))
+ {
+ return self::$instances[$name];
+ }
+
+ $instance = new self();
+
+ self::$instances[$name] = $instance;
+
+ return $instance;
+ }
+
+ private static $instances = array();
+
+ #
+ # Deprecated Methods
+ #
+
+ /**
+ * @deprecated in favor of "text"
+ */
+ function parse($text)
+ {
+ $markup = $this->text($text);
+
+ return $markup;
+ }
+
+ #
+ # Fields
+ #
+
+ protected $Definitions;
+
+ #
+ # Read-only
+
+ protected $specialCharacters = array(
+ '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!',
+ );
+
+ protected $StrongRegex = array(
+ '*' => '/^[*]{2}((?:[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
+ '_' => '/^__((?:[^_]|_[^_]*_)+?)__(?!_)/us',
+ );
+
+ protected $EmRegex = array(
+ '*' => '/^[*]((?:[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
+ '_' => '/^_((?:[^_]|__[^_]*__)+?)_(?!_)\b/us',
+ );
+
+ protected $textLevelElements = array(
+ 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont',
+ 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing',
+ 'i', 'rp', 'del', 'code', 'strike', 'marquee',
+ 'q', 'rt', 'ins', 'font', 'strong',
+ 's', 'tt', 'sub', 'mark',
+ 'u', 'xm', 'sup', 'nobr',
+ 'var', 'ruby',
+ 'wbr', 'span',
+ 'time',
+ );
+}
diff --git a/classes/assignment.class.php b/classes/assignment.class.php
new file mode 100644
index 0000000..e13bd6d
--- /dev/null
+++ b/classes/assignment.class.php
@@ -0,0 +1,316 @@
+<?php
+/**
+ * Provides the assignment class, an interface to the assignment table in the database
+ *
+ * @author Camil Staps
+ *
+ * BusinessAdmin: administrative software for small companies
+ * Copyright (C) 2015 Camil Staps (ViviSoft)
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * An interface to the assignment table in the database
+ */
+class assignment {
+ /**
+ * @var pdo $pdo The PDO class for database communication
+ * @var int $id The id of the assignment
+ * @var int $offerId The id of the offer this assignment is linked to
+ * @var string $title The title of the assignment
+ * @var string $description The description of the assignment
+ * @var int $hours The amount of hours of the assignment
+ * @var float $price_per_hour The price per hour for this assignment, in your valuta
+ * @var float $vat The percentage of VAT to calculate on this assignment
+ */
+ protected $pdo, $offerId, $id, $title, $description, $hours, $price_per_hour, $vat;
+
+ const SUBTOTAL = 1;
+ const VAT = 2;
+ const TOTAL = 3;
+
+ /**
+ * Create a new instance
+ *
+ * @param PDO $pdo The PDO class, to access the database
+ * @param int $id The id of the assignment to fetch
+ *
+ * @throws PDOException If something went wrong with the database
+ * @throws Exception If the assignment could not be found
+ */
+ public function __construct($pdo, $id) {
+ $this->pdo = $pdo;
+
+ $stmt = $this->pdo->prepare("SELECT * FROM `".constants::db_prefix."assignment` WHERE `id`=?");
+ $stmt->execute(array($id));
+ if ($stmt->rowCount() == 0) {
+ throw new Exception("The assignment with id '$id' could not be found.");
+ }
+ $assignment = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ $this->id = $assignment['id'];
+ $this->offerId = $assignment['offerId'];
+ $this->title = $assignment['title'];
+ $this->description = $assignment['description'];
+ $this->hours = $assignment['hours'];
+ $this->price_per_hour = $assignment['price_per_hour'];
+ $this->vat = $assignment['VAT_percentage'];
+ }
+
+ //------------------------------------------------------------------------------
+ // Getters and setters
+ //------------------------------------------------------------------------------
+
+ /**
+ * Get the ID of the assignment
+ *
+ * @return int The ID
+ */
+ public function getId() {
+ return $this->id;
+ }
+
+ /**
+ * Get the ID of the offer that this assignment is linked to
+ *
+ * @return int The ID
+ */
+ public function getOfferId() {
+ return $this->offerId;
+ }
+
+ /**
+ * Get the offer that this assignment is linked to
+ *
+ * @return offer The offer
+ */
+ public function getOffer() {
+ return new offer($this->pdo, $this->offerId);
+ }
+
+ /**
+ * Get the title of the assignment
+ *
+ * @return string The title
+ */
+ public function getTitle() {
+ return $this->title;
+ }
+
+ /**
+ * Set the title of the assignment
+ *
+ * @param string $title The new title for the assignment
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setTitle($title) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."assignment` SET `title`=? WHERE `id`=?");
+ $stmt->execute(array($title, $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->title = $title;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the description of the assignment
+ *
+ * @param bool $parseMarkdown Whether or not to parse markdown
+ *
+ * @return string The description
+ */
+ public function getDescription($parseMarkdown = true) {
+ if ($parseMarkdown) {
+ $pd = new Parsedown;
+ return $pd->text($this->description);
+ } else {
+ return $this->description;
+ }
+ }
+
+ /**
+ * Set the description of the assignment
+ *
+ * @param string $description The new description for the assignment
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setDescription($description) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."assignment` SET `description`=? WHERE `id`=?");
+ $stmt->execute(array($description, $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->description = $description;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the amount of hours of the assignment
+ *
+ * @return int The amount of hours
+ */
+ public function getHours() {
+ return $this->hours;
+ }
+
+ /**
+ * Set the amount of hours of the assignment
+ *
+ * @param int $hours The new amount hours for the assignment
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setHours($hours) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."assignment` SET `hours`=? WHERE `id`=?");
+ $stmt->execute(array($hours, $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->hours = $hours;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the price per hour of the assignment
+ *
+ * @return float The price per hour
+ */
+ public function getPricePerHour() {
+ return $this->price_per_hour;
+ }
+
+ /**
+ * Set the price per hour of the assignment
+ *
+ * @param float $price_per_hour The new price per hour for the assignment
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setPricePerHour($price_per_hour) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."assignment` SET `price_per_hour`=? WHERE `id`=?");
+ $stmt->execute(array($price_per_hour, $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->price_per_hour = $price_per_hour;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the VAT percentage of the assignment
+ *
+ * @return float The VAT percentage
+ */
+ public function getVAT() {
+ return $this->vat;
+ }
+
+ /**
+ * Set the VAT percentage of the assignment
+ *
+ * @param float $vat The new VAT percentage for the assignment
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setVAT($vat) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."assignment` SET `VAT_percentage`=? WHERE `id`=?");
+ $stmt->execute(array($vat, $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->vat = $vat;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ //------------------------------------------------------------------------------
+ // Other functions
+ //------------------------------------------------------------------------------
+
+ /**
+ * Calculate useful numbers about the assignment
+ *
+ * Subtotal: price_per_hour \* hours
+ *
+ * VAT: subtotal \* VAT_percentage
+ *
+ * Total: subtotal + VAT
+ *
+ * @param int $what Any of assignment::SUBTOTAL, assignment::VAT, assignment::TOTAL
+ * @param int $round How many decimals to round on
+ * @param bool $format Whether to format the number nicely (for output) or not (for calculations)
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return float|bool The calculated value rounded on $round decimals, or false when the input is incorrect
+ */
+ public function calculate($what = self::TOTAL, $round = 2, $format = true) {
+ $return = 0;
+ switch ($what) {
+ case self::SUBTOTAL:
+ $return = $this->getPricePerHour() * $this->getHours();
+ break;
+ case self::VAT:
+ $return = $this->calculate(self::SUBTOTAL, $round + 1, false) * $this->getVAT() / 100;
+ break;
+ case self::TOTAL:
+ $return = $this->calculate(self::SUBTOTAL, $round + 1, false) + $this->calculate(self::VAT, $round + 1, false);
+ break;
+ default:
+ return false;
+ }
+ if ($format) {
+ return number_format($return, $round);
+ } else {
+ return round($return, $round);
+ }
+ }
+
+ /**
+ * Remove this assignment from the database
+ *
+ * If this doesn't succeed (i.e. false is returned), that means the assignment was removed manually or by another instance of this class
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on success, false on failure
+ */
+ public function delete() {
+ $stmt = $this->pdo->prepare("DELETE FROM `".constants::db_prefix."assignment` WHERE `id`=?");
+ $stmt->execute(array($this->id));
+ if ($stmt->rowCount() != 1) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/classes/client.class.php b/classes/client.class.php
new file mode 100644
index 0000000..d39cfaf
--- /dev/null
+++ b/classes/client.class.php
@@ -0,0 +1,195 @@
+<?php
+/**
+ * Provides the client class, an interface to the client table in the database
+ *
+ * @author Camil Staps
+ *
+ * BusinessAdmin: administrative software for small companies
+ * Copyright (C) 2015 Camil Staps (ViviSoft)
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * An interface to the client table in the database
+ */
+class client {
+ /**
+ * @var pdo $pdo The PDO class for database communication
+ * @var int $id The id of the client
+ * @var string $name The name of the client
+ */
+ protected $pdo, $id, $name;
+
+ /**
+ * Create a new instance
+ *
+ * @param PDO $pdo The PDO class, to access the database
+ * @param int $id The id of the client to fetch
+ *
+ * @throws PDOException If something went wrong with the database
+ * @throws Exception If the client could not be found
+ */
+ public function __construct($pdo, $id) {
+ $this->pdo = $pdo;
+
+ $stmt = $this->pdo->prepare("SELECT * FROM `".constants::db_prefix."client` WHERE `id`=?");
+ $stmt->execute(array($id));
+ if ($stmt->rowCount() == 0) {
+ throw new Exception("The client with id '$id' could not be found.");
+ }
+ $client = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ $this->id = $client['id'];
+ $this->name = $client['name'];
+ }
+
+ //------------------------------------------------------------------------------
+ // Getters and setters
+ //------------------------------------------------------------------------------
+
+ /**
+ * Get the ID of the client
+ *
+ * @return int The ID
+ */
+ public function getId() {
+ return $this->id;
+ }
+
+ /**
+ * Get the name of the client
+ *
+ * @return string The name
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * Set the name of the client
+ *
+ * @param string $name The new name for the client
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setName($name) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."client` SET `name`=? WHERE `id`=?");
+ $stmt->execute(array($name, $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->name = $name;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get all contact ids for this client
+ *
+ * @see client::getContacts() This funtion returns instances of the contact class instead of just the ids
+ *
+ * @throws PDOException Is something went wrong with the database
+ *
+ * @return int[] The ids
+ */
+ public function getContactIds() {
+ $ids = array();
+ $contacts = $this->pdo->query("SELECT `id` FROM `".constants::db_prefix."contact` WHERE `clientId`={$this->id}")->fetchAll(PDO::FETCH_ASSOC);
+ foreach ($contacts as $contact) {
+ $ids[] = $contact['id'];
+ }
+ return $ids;
+ }
+
+ /**
+ * Get all contacts for this client
+ *
+ * @see client::getContactIds() This function returns just the ids of the contacts, and not instances of the contact class
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return contact[] An array indexed by id of instances of the contact class
+ */
+ public function getContacts() {
+ $ids = $this->getContactIds();
+ $contacts = array();
+ foreach ($ids as $id) {
+ $contacts[$id] = new contact($this->pdo, $id);
+ }
+ return $contacts;
+ }
+
+ //------------------------------------------------------------------------------
+ // Other functions
+ //------------------------------------------------------------------------------
+
+ /**
+ * Remove this client from the database
+ *
+ * If this doesn't succeed (i.e. false is returned), that means the client was removed manually or by another instance of this class
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on success, false on failure
+ */
+ public function delete() {
+ $stmt = $this->pdo->prepare("DELETE FROM `".constants::db_prefix."client` WHERE `id`=?");
+ $stmt->execute(array($this->id));
+ if ($stmt->rowCount() != 1) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Make a new contact for this client
+ *
+ * @param string $name The name for this contact
+ * @param string $email The email for this contact
+ * @param string $address The first address line of this contact (normally street and house number)
+ * @param string $address_2 The second address line of this contact
+ * @param string $postal_code The postal code for this contact
+ * @param string $city The city for this contact
+ * @param string $state The state for this contact
+ * @param string $country The country for this contact
+ *
+ * @throws PDOException If something went wrong with the database
+ * @throws Exception If there was a problem with the input
+ *
+ * @return contact A new instance of the contact class containing the new contact
+ */
+ public function createContact($name, $email, $address, $address_2, $postal_code, $city, $country) {
+ $stmt = $this->pdo->prepare("INSERT INTO `".constants::db_prefix."contact` (`clientId`,`name`,`email`,`address`,`address_2`,`postal_code`,`city`,`country`) VALUES (?,?,?,?,?,?,?,?)");
+ $stmt->execute(array(
+ $this->id,
+ $name,
+ $email,
+ $address,
+ $address_2,
+ $postal_code,
+ $city,
+ $country
+ ));
+ if ($stmt->rowCount() == 1) {
+ return new contact($this->pdo, $this->pdo->lastInsertId());
+ } else {
+ $error = $stmt->errorInfo();
+ throw new Exception($error[2]);
+ }
+ }
+} \ No newline at end of file
diff --git a/classes/constants.class.php b/classes/constants.class.php
new file mode 100644
index 0000000..7afcf50
--- /dev/null
+++ b/classes/constants.class.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * Some constants that can be used on other locations
+ *
+ * @author Camil Staps
+ *
+ * BusinessAdmin: administrative software for small companies
+ * Copyright (C) 2015 Camil Staps (ViviSoft)
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * A class for some constants
+ */
+class constants {
+ /** @const db_prefix A prefix to add to the tables in the database (leave empty for none) */
+ const db_prefix = '';
+
+ /** @const files_folder The folder to store all files (appendices, invoices, etc.) in; with a trailing slash */
+ const files_folder = '/var/www/localhost/BusinessAdmin/files/';
+ /** @const files_folder_external The external URI to this folder; with a trailing slash */
+ const files_folder_external = 'http://localhost/BusinessAdmin/files/';
+ /** @const files_folder_trash The folder inside files_folder to use a trash, without any trailing slashes */
+ const files_folder_trash = 'trash';
+
+ /** @const url_external The external URI to this folder; with a trailing slash */
+ const url_external = 'http://localhost/BusinessAdmin/';
+ /** @const url_internal The URI without the domain name; with a slash at the beginning but not at the end */
+ const url_internal = '/BusinessAdmin';
+
+ /** @const my_name Name of this control panel */
+ const my_name = 'BusinessAdmin';
+
+ /**
+ * @const invoice_name Your name or the name of your business
+ * @const invoice_address_1 First address line
+ * @const invoice_address_2 Second address line
+ * @const invoice_address_3 Third address line
+ * @const invoice_tax_nr Your tax number
+ * @const invoice_iban Your IBAN number
+ * @const invoice_bic The BIC code of your bank
+ * @const invoice_tel_nr Your telephone number
+ * @const invoice_email Your email address
+ * @const invoice_valuta The valuta symbol (will be placed in front of amounts). You can use a symbol like $ or a code like USD
+ */
+ const invoice_name = 'BusinessAdmin';
+ const invoice_address_1 = 'My Street 1';
+ const invoice_address_2 = '12345 My City';
+ const invoice_address_3 = 'My Country';
+ const invoice_tax_nr = 'XX123456789A00';
+ const invoice_iban = 'XX00 ABCD 1234 5678 90';
+ const invoice_bic = 'XXXX XXXX';
+ const invoice_tel_nr = '+31 6 1234 5678';
+ const invoice_email = 'my-email@domain.tld';
+ const invoice_valuta = '$'; // chr(128) for euro
+} \ No newline at end of file
diff --git a/classes/contact.class.php b/classes/contact.class.php
new file mode 100644
index 0000000..299e6df
--- /dev/null
+++ b/classes/contact.class.php
@@ -0,0 +1,378 @@
+<?php
+/**
+ * Provides the contact class, an interface to the contact table in the database
+ *
+ * @author Camil Staps
+ *
+ * BusinessAdmin: administrative software for small companies
+ * Copyright (C) 2015 Camil Staps (ViviSoft)
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * An interface to the contact table in the database
+ */
+class contact {
+ /**
+ * @var PDO $pdo The PDO class for database communication
+ * @var int $id The id of the contact
+ * @var int $clientId The id of the client the contact is linked to
+ * @var string $name The name of the contact
+ * @var string $email The email address of the contact
+ * @var string $address The first address line (normally street and house number) of the contact
+ * @var string $address_2 The second address line (can be null)
+ * @var string $postal_code The postal code of the contact
+ * @var string $city The city of the contact
+ * @var string $country The country of the contact
+ * @var string $language The language of the contact
+ */
+ protected $pdo, $id, $clientId, $name, $email, $address, $postal_code, $city, $country, $language;
+
+ /**
+ * Create a new instance
+ *
+ * @param PDO $pdo The PDO class, to access the database
+ * @param int $id The id of the contact to fetch
+ *
+ * @throws PDOException If something went wrong with the database
+ * @throws Exception If the contact could not be found
+ */
+ public function __construct($pdo, $id) {
+ $this->pdo = $pdo;
+
+ $stmt = $this->pdo->prepare("SELECT * FROM `".constants::db_prefix."contact` WHERE `id`=?");
+ $stmt->execute(array($id));
+ if ($stmt->rowCount() == 0) {
+ throw new Exception("The contact with id '$id' could not be found.");
+ }
+ $contact = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ $this->id = $contact['id'];
+ $this->clientId = $contact['clientId'];
+ $this->name = $contact['name'];
+ $this->email = $contact['email'];
+ $this->address = $contact['address'];
+ $this->address_2 = $contact['address_2'];
+ $this->postal_code = $contact['postal_code'];
+ $this->city = $contact['city'];
+ $this->country = $contact['country'];
+ $this->language = $contact['language'];
+ }
+
+ //------------------------------------------------------------------------------
+ // Getters and setters
+ //------------------------------------------------------------------------------
+
+ /**
+ * Get the ID of the contact
+ *
+ * @return int The ID
+ */
+ public function getId() {
+ return $this->id;
+ }
+
+ /**
+ * Get the ID of the client that this contact is linked to
+ *
+ * @return int The ID
+ */
+ public function getClientId() {
+ return $this->clientId;
+ }
+
+ /**
+ * Get the client that this contact is linked to
+ *
+ * @return client The client
+ */
+ public function getClient() {
+ return new client($this->pdo, $this->clientId);
+ }
+
+ /**
+ * Get the name of the contact
+ *
+ * @return string The name
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * Set the name of the contact
+ *
+ * @param string $name The new name for the contact
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setName($name) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."contact` SET `name`=? WHERE `id`=?");
+ $stmt->execute(array($name, $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->name = $name;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the email of the contact
+ *
+ * @return string The email
+ */
+ public function getEmail() {
+ return $this->email;
+ }
+
+ /**
+ * Set the email of the contact
+ *
+ * @param string $email The new email for the contact
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setEmail($email) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."contact` SET `email`=? WHERE `id`=?");
+ $stmt->execute(array($email, $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->email = $email;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the first address line of the contact
+ *
+ * @return string The address
+ */
+ public function getAddress() {
+ return $this->address;
+ }
+
+ /**
+ * Set the first address line of the contact
+ *
+ * @param string $address The new address for the contact
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setAddress($address) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."contact` SET `address`=? WHERE `id`=?");
+ $stmt->execute(array($address, $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->address = $address;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the second address line of the contact
+ *
+ * @return string The address
+ */
+ public function getAddress_2() {
+ return $this->address_2;
+ }
+
+ /**
+ * Set the second address line of the contact
+ *
+ * @param string $address_2 The new address for the contact
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setAddress_2($address_2) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."contact` SET `address_2`=? WHERE `id`=?");
+ $stmt->execute(array($address_2, $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->address_2 = $address_2;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the postal_code of the contact
+ *
+ * @return string The postal_code
+ */
+ public function getPostalCode() {
+ return $this->postal_code;
+ }
+
+ /**
+ * Set the postal code of the contact
+ *
+ * @param $postal_code string The new postal code for the contact
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setPostalCode($postal_code) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."contact` SET `postal_code`=? WHERE `id`=?");
+ $stmt->execute(array($postal_code, $this->id));
+ if ($stmt->rowCount() == 1) {
+ return true;
+ $this->postal_code = $postal_code;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the city of the contact
+ *
+ * @return string The city
+ */
+ public function getCity() {
+ return $this->city;
+ }
+
+ /**
+ * Set the city of the contact
+ *
+ * @param string $city The new city for the contact
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setCity($city) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."contact` SET `city`=? WHERE `id`=?");
+ $stmt->execute(array($city, $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->city = $city;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the country of the contact
+ *
+ * @return string The country
+ */
+ public function getCountry() {
+ return $this->country;
+ }
+
+ /**
+ * Set the country of the contact
+ *
+ * @param string $country The new country for the contact
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setCountry($country) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."contact` SET `country`=? WHERE `id`=?");
+ $stmt->execute(array($country, $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->country = $country;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the language of the contact
+ *
+ * @return string The language
+ */
+ public function getLanguage() {
+ return $this->language;
+ }
+
+ /**
+ * Set the language of the contact
+ *
+ * @param string $language The new language for the contact
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setLanguage($language) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."contact` SET `language`=? WHERE `id`=?");
+ $stmt->execute(array($language, $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->language = $language;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ //------------------------------------------------------------------------------
+ // Other functions
+ //------------------------------------------------------------------------------
+
+ /**
+ * Remove this contact from the database
+ *
+ * If this doesn't succeed (i.e. false is returned), that means the contact was removed manually or by another instance of this class
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on success, false on failure
+ */
+ public function delete() {
+ $stmt = $this->pdo->prepare("DELETE FROM `".constants::db_prefix."contact` WHERE `id`=?");
+ $stmt->execute(array($this->id));
+ if ($stmt->rowCount() != 1) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Make a new offer for this contact
+ *
+ * @throws PDOException If something went wrong with the database
+ * @throws Exception If there was a problem with the input
+ *
+ * @return offer A new instance of the offer class containing the new offer
+ */
+ public function createOffer() {
+ $stmt = $this->pdo->prepare("INSERT INTO `".constants::db_prefix."offer` (`contactId`) VALUES (?)");
+ $stmt->execute(array($this->id));
+ if ($stmt->rowCount() == 1) {
+ return new offer($this->pdo, $this->pdo->lastInsertId());
+ } else {
+ $error = $stmt->errorInfo();
+ throw new Exception($error[2]);
+ }
+ }
+} \ No newline at end of file
diff --git a/classes/correspondence.class.php b/classes/correspondence.class.php
new file mode 100644
index 0000000..efe4c8d
--- /dev/null
+++ b/classes/correspondence.class.php
@@ -0,0 +1,265 @@
+<?php
+/**
+ * This file provides the correspondence class
+ *
+ * @author Camil Staps
+ *
+ * BusinessAdmin: administrative software for small companies
+ * Copyright (C) 2015 Camil Staps (ViviSoft)
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+require_once('fpdf.php');
+
+/**
+ * An extension of FPDF to generate personalized correspondence PDFs
+ */
+class correspondence extends FPDF {
+ var $contact, $language;
+
+ /**
+ * If you'd want, you could change these constants to encode an RGB colour for headings in your PDFs
+ */
+ const HEAD_RED = 0;
+ const HEAD_GREEN = 0;
+ const HEAD_BLUE = 0;
+
+ /**
+ * Array holding the translations
+ *
+ * @see _() A function to translate
+ */
+ protected static $translations = array(
+ 'adres' => array(
+ 'en' => 'Address',
+ 'nl' => 'Adres'),
+ 'amount' => array(
+ 'en' => 'Amount',
+ 'nl' => 'Subtotaal'),
+ 'amount-due' => array(
+ 'en' => 'Total amount due',
+ 'nl' => 'Te voldoen'),
+ 'biccode' => array(
+ 'en' => 'The BIC code of our bank is %%.',
+ 'nl' => 'De BIC-code van de bank is %%.'),
+ 'btwnr' => array(
+ 'en' => 'VAT nr',
+ 'nl' => 'BTW nr'),
+ 'description' => array(
+ 'en' => 'Description',
+ 'nl' => 'Omschrijving'),
+ 'due-date' => array(
+ 'en' => 'Due date',
+ 'nl' => 'Vervaldatum'),
+ 'email' => array(
+ 'en' => 'Email',
+ 'nl' => 'Email'),
+ 'iban' => array(
+ 'en' => 'IBAN',
+ 'nl' => 'IBAN'),
+ 'invoice' => array(
+ 'en' => 'Invoice',
+ 'nl' => 'Factuur'),
+ 'invoice-date' => array(
+ 'en' => 'Invoice date',
+ 'nl' => 'Factuurdatum'),
+ 'invoice-nr' => array(
+ 'en' => 'Invoice number',
+ 'nl' => 'Factuurnummer'),
+ 'price-excl' => array(
+ 'en' => 'Price excl.',
+ 'nl' => 'Prijs excl.'),
+ 'price-incl' => array(
+ 'en' => 'Price incl.',
+ 'nl' => 'Prijs incl.'),
+ 'request' => array(
+ 'en' => 'You are kindly requested to transfer the total amount before the due date to the provided IBAN nr.',
+ 'nl' => 'U wordt vriendelijk verzocht het te voldoen bedrag voor de vervaldatum van de factuur over te maken naar opgegeven IBAN-nummer.'),
+ 'tel-nr' => array(
+ 'en' => 'Tel.',
+ 'nl' => 'Tel.'),
+ 'total' => array(
+ 'en' => 'Total',
+ 'nl' => 'Totaal'),
+ 'vat' => array(
+ 'en' => 'VAT',
+ 'nl' => 'BTW'),
+ );
+
+ /**
+ * Translate a string
+ *
+ * @see $translations The array holding the translations
+ *
+ * @param string $key The string to translate
+ * @param string $lang The language to translate to (two-letter code)
+ *
+ * @return string The translated string
+ */
+ public function _($key) {
+ if (!array_key_exists($key, self::$translations))
+ return $key;
+ return self::$translations[$key][$this->language];
+ }
+
+ /**
+ * Create a new PDF
+ *
+ * @param string $orientation See the FPDF class specs
+ * @param string $unit See the FPDF class specs
+ * @param string $size See the FPDF class specs
+ */
+ function __construct($orientation='P',$unit='mm',$size='A4') {
+ $this->FPDF($orientation,$unit,$size);
+
+ $this->SetMargins(30,20,20);
+ $this->SetAuthor(constants::invoice_name);
+ $this->SetCreator('BusinessAdmin Correspondence Generator');
+ $this->SetDisplayMode('fullpage','continuous');
+ }
+
+ /**
+ * Set the language of the correspondence (used to translate stuff)
+ *
+ * @param string $lang The language, two letter code ('en', 'nl', ...)
+ */
+ function SetLangauge($lang) {
+ $supported = array('nl', 'en');
+ if (!in_array($lang, $supported)) {
+ throw new Exception("Language not supported");
+ } else {
+ $this->language = $lang;
+ }
+ }
+
+ /**
+ * Set the contact to whom this correspondence will be sent (used for the address)
+ *
+ * @param contact $contact The contact
+ */
+ function SetContact($contact) {
+ $this->contact = $contact;
+
+ $this->language = $contact->getLanguage();
+ }
+
+ /**
+ * Makes a header with your details and the address of the contact
+ */
+ function CorrespondenceHeader() {
+ if (file_exists(constants::files_folder . 'logo-correspondence.png')) {
+ $this->Image(constants::files_folder . 'logo-correspondence.png',30,20,50);
+ }
+
+ $this->SetFont('Helvetica','',7);
+ $this->Cell(110,20);
+ $this->Cell(12,1,' ','T');
+ $this->Cell(2,1);
+ $this->Cell(35,1,' ','T');
+ $this->Ln();
+
+ $this->Cell(110,3.5);
+ $this->SetFont('','B');
+ $this->Cell(12,3.5,$this->_('adres'),'',0,'R');
+ $this->Cell(2,3.5);
+ $this->SetFont('','');
+ $this->Cell(35,3.5, constants::invoice_name);
+ $this->Ln();
+
+ $this->Cell(124,3.5);
+ $this->Cell(35,3.5, constants::invoice_address_1);
+ $this->Ln();
+ $this->Cell(124,3.5);
+ $this->Cell(35,3.5, constants::invoice_address_2);
+ $this->Ln();
+ $this->Cell(124,3.5);
+ $this->Cell(35,3.5, constants::invoice_address_3);
+ $this->Ln();
+ $this->Cell(160,1.5);
+ $this->Ln();
+ $this->Cell(110,3.5);
+ $this->SetFont('','B');
+ $this->Cell(12,3.5,$this->_('btwnr'),'',0,'R');
+ $this->Cell(2,3.5);
+ $this->SetFont('','');
+ $this->Cell(35,3.5, constants::invoice_tax_nr);
+ $this->Ln();
+ $this->Cell(160,1.5);
+ $this->Ln();
+ $this->Cell(110,3.5);
+ $this->SetFont('','B');
+ $this->Cell(12,3.5, $this->_('iban'),'',0,'R');
+ $this->Cell(2,3.5);
+ $this->SetFont('','');
+ $this->Cell(35,3.5, constants::invoice_iban);
+ $this->Ln();
+ $this->Cell(160,1.5);
+ $this->Ln();
+ $this->Cell(110,3.5);
+ $this->SetFont('','B');
+ $this->Cell(12,3.5, $this->_('tel-nr'),'',0,'R');
+ $this->Cell(2,3.5);
+ $this->SetFont('','',7);
+ $this->Cell(35,3.5, constants::invoice_tel_nr);
+ $this->Ln();
+ $this->Cell(110,3.5);
+ $this->SetFont('','B');
+ $this->Cell(12,3.5, $this->_('email'),'',0,'R');
+ $this->Cell(2,3.5);
+ $this->SetFont('','');
+ $this->Cell(35,3.5, constants::invoice_email);
+ $this->Ln();
+ $this->Cell(110,1);
+ $this->Cell(12,1,' ','B');
+ $this->Cell(2,1);
+ $this->Cell(35,1,' ','B');
+ $this->Ln();
+ $this->Cell(160,15);
+ $this->Ln();
+
+ $this->SetFont('','',11);
+ $this->SetXY(30,56.5);
+ $this->Cell(90,5.5,utf8_decode($this->contact->getName()));
+ $this->Ln();
+ $this->SetXY(30,61.5);
+ $this->Cell(90,5.5,utf8_decode($this->contact->getAddress()));
+ $this->Ln();
+ $this->SetXY(30,66.5);
+ $this->Cell(90,5.5,utf8_decode($this->contact->getPostalCode().' '.$this->contact->getCity()));
+ $this->Ln();
+ $this->SetXY(30,71.5);
+ $this->Cell(90,5.5,utf8_decode($this->contact->getCountry()));
+ $this->Ln();
+ }
+
+ /**
+ * Put PDF information
+ */
+ function _putinfo()
+ {
+ $this->_out('/Producer '.$this->_textstring('BusinessAdmin Correspondence Generator'));
+ if(!empty($this->title))
+ $this->_out('/Title '.$this->_textstring($this->title));
+ if(!empty($this->subject))
+ $this->_out('/Subject '.$this->_textstring($this->subject));
+ if(!empty($this->author))
+ $this->_out('/Author '.$this->_textstring($this->author));
+ if(!empty($this->keywords))
+ $this->_out('/Keywords '.$this->_textstring($this->keywords));
+ if(!empty($this->creator))
+ $this->_out('/Creator '.$this->_textstring($this->creator));
+ $this->_out('/CreationDate '.$this->_textstring('D:'.@date('YmdHis')));
+ }
+} \ No newline at end of file
diff --git a/classes/file.class.php b/classes/file.class.php
new file mode 100644
index 0000000..ee1ccce
--- /dev/null
+++ b/classes/file.class.php
@@ -0,0 +1,148 @@
+<?php
+/**
+ * Provides the file class, an interface to the file table in the database
+ *
+ * @author Camil Staps
+ *
+ * BusinessAdmin: administrative software for small companies
+ * Copyright (C) 2015 Camil Staps (ViviSoft)
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * An interface to the file table in the database
+ */
+class file {
+ /**
+ * @var PDO $pdo The PDO class for database communication
+ * @var int $id The id of the file
+ * @var string $filename The relative path to the file
+ */
+ protected $pdo, $id, $filename;
+
+ /**
+ * Create a new instance
+ *
+ * @param PDO $pdo The PDO class, to access the database
+ * @param int $id The id of the file to fetch
+ *
+ * @throws PDOException If something went wrong with the database
+ * @throws Exception If the file could not be found
+ */
+ public function __construct($pdo, $id) {
+ $this->pdo = $pdo;
+
+ $stmt = $this->pdo->prepare("SELECT * FROM `".constants::db_prefix."file` WHERE `id`=?");
+ $stmt->execute(array($id));
+ if ($stmt->rowCount() == 0) {
+ throw new Exception("The file with id '$id' could not be found.");
+ }
+ $file = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ $this->id = $file['id'];
+ $this->filename = $file['filename'];
+ }
+
+ //------------------------------------------------------------------------------
+ // Getters and setters
+ //------------------------------------------------------------------------------
+
+ /**
+ * Get the ID of the file
+ *
+ * @return int The ID
+ */
+ public function getId() {
+ return $this->id;
+ }
+
+ /**
+ * Get the relative filename of the file
+ *
+ * @see file::getFilenamePath To get the full internal path to the file
+ * @see file::getFilenameURI To get the full external path to the file
+ *
+ * @return string The filename
+ */
+ public function getFilename() {
+ return $this->filename;
+ }
+
+ /**
+ * Get the full internal path to the file
+ *
+ * @see file::getFilename To get the relative filename
+ * @see file::getFilenameURI To get the full external path to the file
+ *
+ * @return string The path
+ */
+ public function getFilenamePath() {
+ return constants::files_folder . $this->filename;
+ }
+
+ /**
+ * Get the full external path to the file
+ *
+ * @see file::getFilename To get the relative filename
+ * @see file::getFilenamePath To get the full internal path to the file
+ *
+ * @return string The URI
+ */
+ public function getFilenameURI() {
+ return constants::files_folder_external . $this->filename;
+ }
+
+ //------------------------------------------------------------------------------
+ // Other functions
+ //------------------------------------------------------------------------------
+
+ /**
+ * Move this file to the trash and delete all records for it
+ *
+ * Physically, this moves the file to a trash folder
+ * Then, the file will be removed from the file table in the database
+ * Any appendices linking to this file with fileId will have fileId NULL
+ * Any offers linking to this file with invoice_fileId will have invoice_fileId NULL
+ *
+ * @throws PDOException If there's something wrong with the database
+ *
+ * @return bool True on success, false on failure
+ */
+ public function delete() {
+ // Try to move the file to trash
+ $newname = pathinfo($this->filename, PATHINFO_FILENAME) . '--' . date('Y-m-d.H.i.s.') . pathinfo($this->filename, PATHINFO_EXTENSION);
+ $newdir = pathinfo($this->getFilenamePath(), PATHINFO_DIRNAME) . '/' . constants::files_folder_trash . '/';
+ if (!file_exists($newdir)) {
+ if (!mkdir($newdir)) {
+ return false;
+ }
+ }
+ if (!(@rename($this->getFilenamePath(), $newdir . $newname))) {
+ return false;
+ }
+
+ // Remove offers linked by invoice_fileId
+ $this->pdo->query("UPDATE `".constants::db_prefix."offer` SET `invoice_fileId`=NULL WHERE `invoice_fileId`={$this->id}");
+
+ // Remove the record of the file
+ $stmt = $this->pdo->prepare("DELETE FROM `".constants::db_prefix."file` WHERE `id`=?");
+ $stmt->execute(array($this->id));
+ if ($stmt->rowCount() == 1) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/classes/font/courier.php b/classes/font/courier.php
new file mode 100755
index 0000000..213bf35
--- /dev/null
+++ b/classes/font/courier.php
@@ -0,0 +1,8 @@
+<?php
+$type = 'Core';
+$name = 'Courier';
+$up = -100;
+$ut = 50;
+for($i=0;$i<=255;$i++)
+ $cw[chr($i)] = 600;
+?>
diff --git a/classes/font/courierb.php b/classes/font/courierb.php
new file mode 100755
index 0000000..3fc69a5
--- /dev/null
+++ b/classes/font/courierb.php
@@ -0,0 +1,8 @@
+<?php
+$type = 'Core';
+$name = 'Courier-Bold';
+$up = -100;
+$ut = 50;
+for($i=0;$i<=255;$i++)
+ $cw[chr($i)] = 600;
+?>
diff --git a/classes/font/courierbi.php b/classes/font/courierbi.php
new file mode 100755
index 0000000..a49f2ae
--- /dev/null
+++ b/classes/font/courierbi.php
@@ -0,0 +1,8 @@
+<?php
+$type = 'Core';
+$name = 'Courier-BoldOblique';
+$up = -100;
+$ut = 50;
+for($i=0;$i<=255;$i++)
+ $cw[chr($i)] = 600;
+?>
diff --git a/classes/font/courieri.php b/classes/font/courieri.php
new file mode 100755
index 0000000..9c1c2cf
--- /dev/null
+++ b/classes/font/courieri.php
@@ -0,0 +1,8 @@
+<?php
+$type = 'Core';
+$name = 'Courier-Oblique';
+$up = -100;
+$ut = 50;
+for($i=0;$i<=255;$i++)
+ $cw[chr($i)] = 600;
+?>
diff --git a/classes/font/helvetica.php b/classes/font/helvetica.php
new file mode 100755
index 0000000..7e20c3a
--- /dev/null
+++ b/classes/font/helvetica.php
@@ -0,0 +1,19 @@
+<?php
+$type = 'Core';
+$name = 'Helvetica';
+$up = -100;
+$ut = 50;
+$cw = array(
+ chr(0)=>278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278,
+ chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>278,'"'=>355,'#'=>556,'$'=>556,'%'=>889,'&'=>667,'\''=>191,'('=>333,')'=>333,'*'=>389,'+'=>584,
+ ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>278,';'=>278,'<'=>584,'='=>584,'>'=>584,'?'=>556,'@'=>1015,'A'=>667,
+ 'B'=>667,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>500,'K'=>667,'L'=>556,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944,
+ 'X'=>667,'Y'=>667,'Z'=>611,'['=>278,'\\'=>278,']'=>278,'^'=>469,'_'=>556,'`'=>333,'a'=>556,'b'=>556,'c'=>500,'d'=>556,'e'=>556,'f'=>278,'g'=>556,'h'=>556,'i'=>222,'j'=>222,'k'=>500,'l'=>222,'m'=>833,
+ 'n'=>556,'o'=>556,'p'=>556,'q'=>556,'r'=>333,'s'=>500,'t'=>278,'u'=>556,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>500,'{'=>334,'|'=>260,'}'=>334,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>222,chr(131)=>556,
+ chr(132)=>333,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>222,chr(146)=>222,chr(147)=>333,chr(148)=>333,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000,
+ chr(154)=>500,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>260,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333,
+ chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>556,chr(182)=>537,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>667,chr(193)=>667,chr(194)=>667,chr(195)=>667,chr(196)=>667,chr(197)=>667,
+ chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722,
+ chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>500,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>556,chr(241)=>556,
+ chr(242)=>556,chr(243)=>556,chr(244)=>556,chr(245)=>556,chr(246)=>556,chr(247)=>584,chr(248)=>611,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>500,chr(254)=>556,chr(255)=>500);
+?>
diff --git a/classes/font/helveticab.php b/classes/font/helveticab.php
new file mode 100755
index 0000000..452e0ac
--- /dev/null
+++ b/classes/font/helveticab.php
@@ -0,0 +1,19 @@
+<?php
+$type = 'Core';
+$name = 'Helvetica-Bold';
+$up = -100;
+$ut = 50;
+$cw = array(
+ chr(0)=>278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278,
+ chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>333,'"'=>474,'#'=>556,'$'=>556,'%'=>889,'&'=>722,'\''=>238,'('=>333,')'=>333,'*'=>389,'+'=>584,
+ ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>333,';'=>333,'<'=>584,'='=>584,'>'=>584,'?'=>611,'@'=>975,'A'=>722,
+ 'B'=>722,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>556,'K'=>722,'L'=>611,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944,
+ 'X'=>667,'Y'=>667,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>584,'_'=>556,'`'=>333,'a'=>556,'b'=>611,'c'=>556,'d'=>611,'e'=>556,'f'=>333,'g'=>611,'h'=>611,'i'=>278,'j'=>278,'k'=>556,'l'=>278,'m'=>889,
+ 'n'=>611,'o'=>611,'p'=>611,'q'=>611,'r'=>389,'s'=>556,'t'=>333,'u'=>611,'v'=>556,'w'=>778,'x'=>556,'y'=>556,'z'=>500,'{'=>389,'|'=>280,'}'=>389,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>278,chr(131)=>556,
+ chr(132)=>500,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>278,chr(146)=>278,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000,
+ chr(154)=>556,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>280,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333,
+ chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>611,chr(182)=>556,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722,
+ chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722,
+ chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>556,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>611,chr(241)=>611,
+ chr(242)=>611,chr(243)=>611,chr(244)=>611,chr(245)=>611,chr(246)=>611,chr(247)=>584,chr(248)=>611,chr(249)=>611,chr(250)=>611,chr(251)=>611,chr(252)=>611,chr(253)=>556,chr(254)=>611,chr(255)=>556);
+?>
diff --git a/classes/font/helveticabi.php b/classes/font/helveticabi.php
new file mode 100755
index 0000000..ea5c56f
--- /dev/null
+++ b/classes/font/helveticabi.php
@@ -0,0 +1,19 @@
+<?php
+$type = 'Core';
+$name = 'Helvetica-BoldOblique';
+$up = -100;
+$ut = 50;
+$cw = array(
+ chr(0)=>278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278,
+ chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>333,'"'=>474,'#'=>556,'$'=>556,'%'=>889,'&'=>722,'\''=>238,'('=>333,')'=>333,'*'=>389,'+'=>584,
+ ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>333,';'=>333,'<'=>584,'='=>584,'>'=>584,'?'=>611,'@'=>975,'A'=>722,
+ 'B'=>722,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>556,'K'=>722,'L'=>611,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944,
+ 'X'=>667,'Y'=>667,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>584,'_'=>556,'`'=>333,'a'=>556,'b'=>611,'c'=>556,'d'=>611,'e'=>556,'f'=>333,'g'=>611,'h'=>611,'i'=>278,'j'=>278,'k'=>556,'l'=>278,'m'=>889,
+ 'n'=>611,'o'=>611,'p'=>611,'q'=>611,'r'=>389,'s'=>556,'t'=>333,'u'=>611,'v'=>556,'w'=>778,'x'=>556,'y'=>556,'z'=>500,'{'=>389,'|'=>280,'}'=>389,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>278,chr(131)=>556,
+ chr(132)=>500,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>278,chr(146)=>278,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000,
+ chr(154)=>556,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>280,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333,
+ chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>611,chr(182)=>556,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722,
+ chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722,
+ chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>556,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>611,chr(241)=>611,
+ chr(242)=>611,chr(243)=>611,chr(244)=>611,chr(245)=>611,chr(246)=>611,chr(247)=>584,chr(248)=>611,chr(249)=>611,chr(250)=>611,chr(251)=>611,chr(252)=>611,chr(253)=>556,chr(254)=>611,chr(255)=>556);
+?>
diff --git a/classes/font/helveticai.php b/classes/font/helveticai.php
new file mode 100755
index 0000000..e3c638a
--- /dev/null
+++ b/classes/font/helveticai.php
@@ -0,0 +1,19 @@
+<?php
+$type = 'Core';
+$name = 'Helvetica-Oblique';
+$up = -100;
+$ut = 50;
+$cw = array(
+ chr(0)=>278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278,
+ chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>278,'"'=>355,'#'=>556,'$'=>556,'%'=>889,'&'=>667,'\''=>191,'('=>333,')'=>333,'*'=>389,'+'=>584,
+ ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>278,';'=>278,'<'=>584,'='=>584,'>'=>584,'?'=>556,'@'=>1015,'A'=>667,
+ 'B'=>667,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>500,'K'=>667,'L'=>556,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944,
+ 'X'=>667,'Y'=>667,'Z'=>611,'['=>278,'\\'=>278,']'=>278,'^'=>469,'_'=>556,'`'=>333,'a'=>556,'b'=>556,'c'=>500,'d'=>556,'e'=>556,'f'=>278,'g'=>556,'h'=>556,'i'=>222,'j'=>222,'k'=>500,'l'=>222,'m'=>833,
+ 'n'=>556,'o'=>556,'p'=>556,'q'=>556,'r'=>333,'s'=>500,'t'=>278,'u'=>556,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>500,'{'=>334,'|'=>260,'}'=>334,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>222,chr(131)=>556,
+ chr(132)=>333,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>222,chr(146)=>222,chr(147)=>333,chr(148)=>333,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000,
+ chr(154)=>500,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>260,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333,
+ chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>556,chr(182)=>537,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>667,chr(193)=>667,chr(194)=>667,chr(195)=>667,chr(196)=>667,chr(197)=>667,
+ chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722,
+ chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>500,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>556,chr(241)=>556,
+ chr(242)=>556,chr(243)=>556,chr(244)=>556,chr(245)=>556,chr(246)=>556,chr(247)=>584,chr(248)=>611,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>500,chr(254)=>556,chr(255)=>500);
+?>
diff --git a/classes/font/symbol.php b/classes/font/symbol.php
new file mode 100755
index 0000000..b980b07
--- /dev/null
+++ b/classes/font/symbol.php
@@ -0,0 +1,19 @@
+<?php
+$type = 'Core';
+$name = 'Symbol';
+$up = -100;
+$ut = 50;
+$cw = array(
+ chr(0)=>250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250,
+ chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>713,'#'=>500,'$'=>549,'%'=>833,'&'=>778,'\''=>439,'('=>333,')'=>333,'*'=>500,'+'=>549,
+ ','=>250,'-'=>549,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>278,';'=>278,'<'=>549,'='=>549,'>'=>549,'?'=>444,'@'=>549,'A'=>722,
+ 'B'=>667,'C'=>722,'D'=>612,'E'=>611,'F'=>763,'G'=>603,'H'=>722,'I'=>333,'J'=>631,'K'=>722,'L'=>686,'M'=>889,'N'=>722,'O'=>722,'P'=>768,'Q'=>741,'R'=>556,'S'=>592,'T'=>611,'U'=>690,'V'=>439,'W'=>768,
+ 'X'=>645,'Y'=>795,'Z'=>611,'['=>333,'\\'=>863,']'=>333,'^'=>658,'_'=>500,'`'=>500,'a'=>631,'b'=>549,'c'=>549,'d'=>494,'e'=>439,'f'=>521,'g'=>411,'h'=>603,'i'=>329,'j'=>603,'k'=>549,'l'=>549,'m'=>576,
+ 'n'=>521,'o'=>549,'p'=>549,'q'=>521,'r'=>549,'s'=>603,'t'=>439,'u'=>576,'v'=>713,'w'=>686,'x'=>493,'y'=>686,'z'=>494,'{'=>480,'|'=>200,'}'=>480,'~'=>549,chr(127)=>0,chr(128)=>0,chr(129)=>0,chr(130)=>0,chr(131)=>0,
+ chr(132)=>0,chr(133)=>0,chr(134)=>0,chr(135)=>0,chr(136)=>0,chr(137)=>0,chr(138)=>0,chr(139)=>0,chr(140)=>0,chr(141)=>0,chr(142)=>0,chr(143)=>0,chr(144)=>0,chr(145)=>0,chr(146)=>0,chr(147)=>0,chr(148)=>0,chr(149)=>0,chr(150)=>0,chr(151)=>0,chr(152)=>0,chr(153)=>0,
+ chr(154)=>0,chr(155)=>0,chr(156)=>0,chr(157)=>0,chr(158)=>0,chr(159)=>0,chr(160)=>750,chr(161)=>620,chr(162)=>247,chr(163)=>549,chr(164)=>167,chr(165)=>713,chr(166)=>500,chr(167)=>753,chr(168)=>753,chr(169)=>753,chr(170)=>753,chr(171)=>1042,chr(172)=>987,chr(173)=>603,chr(174)=>987,chr(175)=>603,
+ chr(176)=>400,chr(177)=>549,chr(178)=>411,chr(179)=>549,chr(180)=>549,chr(181)=>713,chr(182)=>494,chr(183)=>460,chr(184)=>549,chr(185)=>549,chr(186)=>549,chr(187)=>549,chr(188)=>1000,chr(189)=>603,chr(190)=>1000,chr(191)=>658,chr(192)=>823,chr(193)=>686,chr(194)=>795,chr(195)=>987,chr(196)=>768,chr(197)=>768,
+ chr(198)=>823,chr(199)=>768,chr(200)=>768,chr(201)=>713,chr(202)=>713,chr(203)=>713,chr(204)=>713,chr(205)=>713,chr(206)=>713,chr(207)=>713,chr(208)=>768,chr(209)=>713,chr(210)=>790,chr(211)=>790,chr(212)=>890,chr(213)=>823,chr(214)=>549,chr(215)=>250,chr(216)=>713,chr(217)=>603,chr(218)=>603,chr(219)=>1042,
+ chr(220)=>987,chr(221)=>603,chr(222)=>987,chr(223)=>603,chr(224)=>494,chr(225)=>329,chr(226)=>790,chr(227)=>790,chr(228)=>786,chr(229)=>713,chr(230)=>384,chr(231)=>384,chr(232)=>384,chr(233)=>384,chr(234)=>384,chr(235)=>384,chr(236)=>494,chr(237)=>494,chr(238)=>494,chr(239)=>494,chr(240)=>0,chr(241)=>329,
+ chr(242)=>274,chr(243)=>686,chr(244)=>686,chr(245)=>686,chr(246)=>384,chr(247)=>384,chr(248)=>384,chr(249)=>384,chr(250)=>384,chr(251)=>384,chr(252)=>494,chr(253)=>494,chr(254)=>494,chr(255)=>0);
+?>
diff --git a/classes/font/times.php b/classes/font/times.php
new file mode 100755
index 0000000..d3ea808
--- /dev/null
+++ b/classes/font/times.php
@@ -0,0 +1,19 @@
+<?php
+$type = 'Core';
+$name = 'Times-Roman';
+$up = -100;
+$ut = 50;
+$cw = array(
+ chr(0)=>250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250,
+ chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>408,'#'=>500,'$'=>500,'%'=>833,'&'=>778,'\''=>180,'('=>333,')'=>333,'*'=>500,'+'=>564,
+ ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>278,';'=>278,'<'=>564,'='=>564,'>'=>564,'?'=>444,'@'=>921,'A'=>722,
+ 'B'=>667,'C'=>667,'D'=>722,'E'=>611,'F'=>556,'G'=>722,'H'=>722,'I'=>333,'J'=>389,'K'=>722,'L'=>611,'M'=>889,'N'=>722,'O'=>722,'P'=>556,'Q'=>722,'R'=>667,'S'=>556,'T'=>611,'U'=>722,'V'=>722,'W'=>944,
+ 'X'=>722,'Y'=>722,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>469,'_'=>500,'`'=>333,'a'=>444,'b'=>500,'c'=>444,'d'=>500,'e'=>444,'f'=>333,'g'=>500,'h'=>500,'i'=>278,'j'=>278,'k'=>500,'l'=>278,'m'=>778,
+ 'n'=>500,'o'=>500,'p'=>500,'q'=>500,'r'=>333,'s'=>389,'t'=>278,'u'=>500,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>444,'{'=>480,'|'=>200,'}'=>480,'~'=>541,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500,
+ chr(132)=>444,chr(133)=>1000,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>556,chr(139)=>333,chr(140)=>889,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>444,chr(148)=>444,chr(149)=>350,chr(150)=>500,chr(151)=>1000,chr(152)=>333,chr(153)=>980,
+ chr(154)=>389,chr(155)=>333,chr(156)=>722,chr(157)=>350,chr(158)=>444,chr(159)=>722,chr(160)=>250,chr(161)=>333,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>200,chr(167)=>500,chr(168)=>333,chr(169)=>760,chr(170)=>276,chr(171)=>500,chr(172)=>564,chr(173)=>333,chr(174)=>760,chr(175)=>333,
+ chr(176)=>400,chr(177)=>564,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>500,chr(182)=>453,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>310,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>444,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722,
+ chr(198)=>889,chr(199)=>667,chr(200)=>611,chr(201)=>611,chr(202)=>611,chr(203)=>611,chr(204)=>333,chr(205)=>333,chr(206)=>333,chr(207)=>333,chr(208)=>722,chr(209)=>722,chr(210)=>722,chr(211)=>722,chr(212)=>722,chr(213)=>722,chr(214)=>722,chr(215)=>564,chr(216)=>722,chr(217)=>722,chr(218)=>722,chr(219)=>722,
+ chr(220)=>722,chr(221)=>722,chr(222)=>556,chr(223)=>500,chr(224)=>444,chr(225)=>444,chr(226)=>444,chr(227)=>444,chr(228)=>444,chr(229)=>444,chr(230)=>667,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>500,
+ chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>564,chr(248)=>500,chr(249)=>500,chr(250)=>500,chr(251)=>500,chr(252)=>500,chr(253)=>500,chr(254)=>500,chr(255)=>500);
+?>
diff --git a/classes/font/timesb.php b/classes/font/timesb.php
new file mode 100755
index 0000000..1c198f0
--- /dev/null
+++ b/classes/font/timesb.php
@@ -0,0 +1,19 @@
+<?php
+$type = 'Core';
+$name = 'Times-Bold';
+$up = -100;
+$ut = 50;
+$cw = array(
+ chr(0)=>250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250,
+ chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>555,'#'=>500,'$'=>500,'%'=>1000,'&'=>833,'\''=>278,'('=>333,')'=>333,'*'=>500,'+'=>570,
+ ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>333,';'=>333,'<'=>570,'='=>570,'>'=>570,'?'=>500,'@'=>930,'A'=>722,
+ 'B'=>667,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>778,'I'=>389,'J'=>500,'K'=>778,'L'=>667,'M'=>944,'N'=>722,'O'=>778,'P'=>611,'Q'=>778,'R'=>722,'S'=>556,'T'=>667,'U'=>722,'V'=>722,'W'=>1000,
+ 'X'=>722,'Y'=>722,'Z'=>667,'['=>333,'\\'=>278,']'=>333,'^'=>581,'_'=>500,'`'=>333,'a'=>500,'b'=>556,'c'=>444,'d'=>556,'e'=>444,'f'=>333,'g'=>500,'h'=>556,'i'=>278,'j'=>333,'k'=>556,'l'=>278,'m'=>833,
+ 'n'=>556,'o'=>500,'p'=>556,'q'=>556,'r'=>444,'s'=>389,'t'=>333,'u'=>556,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>444,'{'=>394,'|'=>220,'}'=>394,'~'=>520,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500,
+ chr(132)=>500,chr(133)=>1000,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>556,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>667,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>500,chr(151)=>1000,chr(152)=>333,chr(153)=>1000,
+ chr(154)=>389,chr(155)=>333,chr(156)=>722,chr(157)=>350,chr(158)=>444,chr(159)=>722,chr(160)=>250,chr(161)=>333,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>220,chr(167)=>500,chr(168)=>333,chr(169)=>747,chr(170)=>300,chr(171)=>500,chr(172)=>570,chr(173)=>333,chr(174)=>747,chr(175)=>333,
+ chr(176)=>400,chr(177)=>570,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>556,chr(182)=>540,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>330,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>500,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722,
+ chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>389,chr(205)=>389,chr(206)=>389,chr(207)=>389,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>570,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722,
+ chr(220)=>722,chr(221)=>722,chr(222)=>611,chr(223)=>556,chr(224)=>500,chr(225)=>500,chr(226)=>500,chr(227)=>500,chr(228)=>500,chr(229)=>500,chr(230)=>722,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>556,
+ chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>570,chr(248)=>500,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>500,chr(254)=>556,chr(255)=>500);
+?>
diff --git a/classes/font/timesbi.php b/classes/font/timesbi.php
new file mode 100755
index 0000000..a6034b2
--- /dev/null
+++ b/classes/font/timesbi.php
@@ -0,0 +1,19 @@
+<?php
+$type = 'Core';
+$name = 'Times-BoldItalic';
+$up = -100;
+$ut = 50;
+$cw = array(
+ chr(0)=>250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250,
+ chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>389,'"'=>555,'#'=>500,'$'=>500,'%'=>833,'&'=>778,'\''=>278,'('=>333,')'=>333,'*'=>500,'+'=>570,
+ ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>333,';'=>333,'<'=>570,'='=>570,'>'=>570,'?'=>500,'@'=>832,'A'=>667,
+ 'B'=>667,'C'=>667,'D'=>722,'E'=>667,'F'=>667,'G'=>722,'H'=>778,'I'=>389,'J'=>500,'K'=>667,'L'=>611,'M'=>889,'N'=>722,'O'=>722,'P'=>611,'Q'=>722,'R'=>667,'S'=>556,'T'=>611,'U'=>722,'V'=>667,'W'=>889,
+ 'X'=>667,'Y'=>611,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>570,'_'=>500,'`'=>333,'a'=>500,'b'=>500,'c'=>444,'d'=>500,'e'=>444,'f'=>333,'g'=>500,'h'=>556,'i'=>278,'j'=>278,'k'=>500,'l'=>278,'m'=>778,
+ 'n'=>556,'o'=>500,'p'=>500,'q'=>500,'r'=>389,'s'=>389,'t'=>278,'u'=>556,'v'=>444,'w'=>667,'x'=>500,'y'=>444,'z'=>389,'{'=>348,'|'=>220,'}'=>348,'~'=>570,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500,
+ chr(132)=>500,chr(133)=>1000,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>556,chr(139)=>333,chr(140)=>944,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>500,chr(151)=>1000,chr(152)=>333,chr(153)=>1000,
+ chr(154)=>389,chr(155)=>333,chr(156)=>722,chr(157)=>350,chr(158)=>389,chr(159)=>611,chr(160)=>250,chr(161)=>389,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>220,chr(167)=>500,chr(168)=>333,chr(169)=>747,chr(170)=>266,chr(171)=>500,chr(172)=>606,chr(173)=>333,chr(174)=>747,chr(175)=>333,
+ chr(176)=>400,chr(177)=>570,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>576,chr(182)=>500,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>300,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>500,chr(192)=>667,chr(193)=>667,chr(194)=>667,chr(195)=>667,chr(196)=>667,chr(197)=>667,
+ chr(198)=>944,chr(199)=>667,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>389,chr(205)=>389,chr(206)=>389,chr(207)=>389,chr(208)=>722,chr(209)=>722,chr(210)=>722,chr(211)=>722,chr(212)=>722,chr(213)=>722,chr(214)=>722,chr(215)=>570,chr(216)=>722,chr(217)=>722,chr(218)=>722,chr(219)=>722,
+ chr(220)=>722,chr(221)=>611,chr(222)=>611,chr(223)=>500,chr(224)=>500,chr(225)=>500,chr(226)=>500,chr(227)=>500,chr(228)=>500,chr(229)=>500,chr(230)=>722,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>556,
+ chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>570,chr(248)=>500,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>444,chr(254)=>500,chr(255)=>444);
+?>
diff --git a/classes/font/timesi.php b/classes/font/timesi.php
new file mode 100755
index 0000000..bd9e0d9
--- /dev/null
+++ b/classes/font/timesi.php
@@ -0,0 +1,19 @@
+<?php
+$type = 'Core';
+$name = 'Times-Italic';
+$up = -100;
+$ut = 50;
+$cw = array(
+ chr(0)=>250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250,
+ chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>420,'#'=>500,'$'=>500,'%'=>833,'&'=>778,'\''=>214,'('=>333,')'=>333,'*'=>500,'+'=>675,
+ ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>333,';'=>333,'<'=>675,'='=>675,'>'=>675,'?'=>500,'@'=>920,'A'=>611,
+ 'B'=>611,'C'=>667,'D'=>722,'E'=>611,'F'=>611,'G'=>722,'H'=>722,'I'=>333,'J'=>444,'K'=>667,'L'=>556,'M'=>833,'N'=>667,'O'=>722,'P'=>611,'Q'=>722,'R'=>611,'S'=>500,'T'=>556,'U'=>722,'V'=>611,'W'=>833,
+ 'X'=>611,'Y'=>556,'Z'=>556,'['=>389,'\\'=>278,']'=>389,'^'=>422,'_'=>500,'`'=>333,'a'=>500,'b'=>500,'c'=>444,'d'=>500,'e'=>444,'f'=>278,'g'=>500,'h'=>500,'i'=>278,'j'=>278,'k'=>444,'l'=>278,'m'=>722,
+ 'n'=>500,'o'=>500,'p'=>500,'q'=>500,'r'=>389,'s'=>389,'t'=>278,'u'=>500,'v'=>444,'w'=>667,'x'=>444,'y'=>444,'z'=>389,'{'=>400,'|'=>275,'}'=>400,'~'=>541,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500,
+ chr(132)=>556,chr(133)=>889,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>500,chr(139)=>333,chr(140)=>944,chr(141)=>350,chr(142)=>556,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>556,chr(148)=>556,chr(149)=>350,chr(150)=>500,chr(151)=>889,chr(152)=>333,chr(153)=>980,
+ chr(154)=>389,chr(155)=>333,chr(156)=>667,chr(157)=>350,chr(158)=>389,chr(159)=>556,chr(160)=>250,chr(161)=>389,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>275,chr(167)=>500,chr(168)=>333,chr(169)=>760,chr(170)=>276,chr(171)=>500,chr(172)=>675,chr(173)=>333,chr(174)=>760,chr(175)=>333,
+ chr(176)=>400,chr(177)=>675,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>500,chr(182)=>523,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>310,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>500,chr(192)=>611,chr(193)=>611,chr(194)=>611,chr(195)=>611,chr(196)=>611,chr(197)=>611,
+ chr(198)=>889,chr(199)=>667,chr(200)=>611,chr(201)=>611,chr(202)=>611,chr(203)=>611,chr(204)=>333,chr(205)=>333,chr(206)=>333,chr(207)=>333,chr(208)=>722,chr(209)=>667,chr(210)=>722,chr(211)=>722,chr(212)=>722,chr(213)=>722,chr(214)=>722,chr(215)=>675,chr(216)=>722,chr(217)=>722,chr(218)=>722,chr(219)=>722,
+ chr(220)=>722,chr(221)=>556,chr(222)=>611,chr(223)=>500,chr(224)=>500,chr(225)=>500,chr(226)=>500,chr(227)=>500,chr(228)=>500,chr(229)=>500,chr(230)=>667,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>500,
+ chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>675,chr(248)=>500,chr(249)=>500,chr(250)=>500,chr(251)=>500,chr(252)=>500,chr(253)=>444,chr(254)=>500,chr(255)=>444);
+?>
diff --git a/classes/font/zapfdingbats.php b/classes/font/zapfdingbats.php
new file mode 100755
index 0000000..afef4d3
--- /dev/null
+++ b/classes/font/zapfdingbats.php
@@ -0,0 +1,19 @@
+<?php
+$type = 'Core';
+$name = 'ZapfDingbats';
+$up = -100;
+$ut = 50;
+$cw = array(
+ chr(0)=>0,chr(1)=>0,chr(2)=>0,chr(3)=>0,chr(4)=>0,chr(5)=>0,chr(6)=>0,chr(7)=>0,chr(8)=>0,chr(9)=>0,chr(10)=>0,chr(11)=>0,chr(12)=>0,chr(13)=>0,chr(14)=>0,chr(15)=>0,chr(16)=>0,chr(17)=>0,chr(18)=>0,chr(19)=>0,chr(20)=>0,chr(21)=>0,
+ chr(22)=>0,chr(23)=>0,chr(24)=>0,chr(25)=>0,chr(26)=>0,chr(27)=>0,chr(28)=>0,chr(29)=>0,chr(30)=>0,chr(31)=>0,' '=>278,'!'=>974,'"'=>961,'#'=>974,'$'=>980,'%'=>719,'&'=>789,'\''=>790,'('=>791,')'=>690,'*'=>960,'+'=>939,
+ ','=>549,'-'=>855,'.'=>911,'/'=>933,'0'=>911,'1'=>945,'2'=>974,'3'=>755,'4'=>846,'5'=>762,'6'=>761,'7'=>571,'8'=>677,'9'=>763,':'=>760,';'=>759,'<'=>754,'='=>494,'>'=>552,'?'=>537,'@'=>577,'A'=>692,
+ 'B'=>786,'C'=>788,'D'=>788,'E'=>790,'F'=>793,'G'=>794,'H'=>816,'I'=>823,'J'=>789,'K'=>841,'L'=>823,'M'=>833,'N'=>816,'O'=>831,'P'=>923,'Q'=>744,'R'=>723,'S'=>749,'T'=>790,'U'=>792,'V'=>695,'W'=>776,
+ 'X'=>768,'Y'=>792,'Z'=>759,'['=>707,'\\'=>708,']'=>682,'^'=>701,'_'=>826,'`'=>815,'a'=>789,'b'=>789,'c'=>707,'d'=>687,'e'=>696,'f'=>689,'g'=>786,'h'=>787,'i'=>713,'j'=>791,'k'=>785,'l'=>791,'m'=>873,
+ 'n'=>761,'o'=>762,'p'=>762,'q'=>759,'r'=>759,'s'=>892,'t'=>892,'u'=>788,'v'=>784,'w'=>438,'x'=>138,'y'=>277,'z'=>415,'{'=>392,'|'=>392,'}'=>668,'~'=>668,chr(127)=>0,chr(128)=>390,chr(129)=>390,chr(130)=>317,chr(131)=>317,
+ chr(132)=>276,chr(133)=>276,chr(134)=>509,chr(135)=>509,chr(136)=>410,chr(137)=>410,chr(138)=>234,chr(139)=>234,chr(140)=>334,chr(141)=>334,chr(142)=>0,chr(143)=>0,chr(144)=>0,chr(145)=>0,chr(146)=>0,chr(147)=>0,chr(148)=>0,chr(149)=>0,chr(150)=>0,chr(151)=>0,chr(152)=>0,chr(153)=>0,
+ chr(154)=>0,chr(155)=>0,chr(156)=>0,chr(157)=>0,chr(158)=>0,chr(159)=>0,chr(160)=>0,chr(161)=>732,chr(162)=>544,chr(163)=>544,chr(164)=>910,chr(165)=>667,chr(166)=>760,chr(167)=>760,chr(168)=>776,chr(169)=>595,chr(170)=>694,chr(171)=>626,chr(172)=>788,chr(173)=>788,chr(174)=>788,chr(175)=>788,
+ chr(176)=>788,chr(177)=>788,chr(178)=>788,chr(179)=>788,chr(180)=>788,chr(181)=>788,chr(182)=>788,chr(183)=>788,chr(184)=>788,chr(185)=>788,chr(186)=>788,chr(187)=>788,chr(188)=>788,chr(189)=>788,chr(190)=>788,chr(191)=>788,chr(192)=>788,chr(193)=>788,chr(194)=>788,chr(195)=>788,chr(196)=>788,chr(197)=>788,
+ chr(198)=>788,chr(199)=>788,chr(200)=>788,chr(201)=>788,chr(202)=>788,chr(203)=>788,chr(204)=>788,chr(205)=>788,chr(206)=>788,chr(207)=>788,chr(208)=>788,chr(209)=>788,chr(210)=>788,chr(211)=>788,chr(212)=>894,chr(213)=>838,chr(214)=>1016,chr(215)=>458,chr(216)=>748,chr(217)=>924,chr(218)=>748,chr(219)=>918,
+ chr(220)=>927,chr(221)=>928,chr(222)=>928,chr(223)=>834,chr(224)=>873,chr(225)=>828,chr(226)=>924,chr(227)=>924,chr(228)=>917,chr(229)=>930,chr(230)=>931,chr(231)=>463,chr(232)=>883,chr(233)=>836,chr(234)=>836,chr(235)=>867,chr(236)=>867,chr(237)=>696,chr(238)=>696,chr(239)=>874,chr(240)=>0,chr(241)=>874,
+ chr(242)=>760,chr(243)=>946,chr(244)=>771,chr(245)=>865,chr(246)=>771,chr(247)=>888,chr(248)=>967,chr(249)=>888,chr(250)=>831,chr(251)=>873,chr(252)=>927,chr(253)=>970,chr(254)=>918,chr(255)=>0);
+?>
diff --git a/classes/fpdf.license.txt b/classes/fpdf.license.txt
new file mode 100755
index 0000000..fd811c6
--- /dev/null
+++ b/classes/fpdf.license.txt
@@ -0,0 +1,6 @@
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software to use, copy, modify, distribute, sublicense, and/or sell
+copies of the software, and to permit persons to whom the software is furnished
+to do so.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. \ No newline at end of file
diff --git a/classes/fpdf.php b/classes/fpdf.php
new file mode 100755
index 0000000..0dd1cb6
--- /dev/null
+++ b/classes/fpdf.php
@@ -0,0 +1,1804 @@
+<?php
+/*******************************************************************************
+* FPDF *
+* *
+* Version: 1.7 *
+* Date: 2011-06-18 *
+* Author: Olivier PLATHEY *
+*******************************************************************************/
+
+define('FPDF_VERSION','1.7');
+
+class FPDF
+{
+var $page; // current page number
+var $n; // current object number
+var $offsets; // array of object offsets
+var $buffer; // buffer holding in-memory PDF
+var $pages; // array containing pages
+var $state; // current document state
+var $compress; // compression flag
+var $k; // scale factor (number of points in user unit)
+var $DefOrientation; // default orientation
+var $CurOrientation; // current orientation
+var $StdPageSizes; // standard page sizes
+var $DefPageSize; // default page size
+var $CurPageSize; // current page size
+var $PageSizes; // used for pages with non default sizes or orientations
+var $wPt, $hPt; // dimensions of current page in points
+var $w, $h; // dimensions of current page in user unit
+var $lMargin; // left margin
+var $tMargin; // top margin
+var $rMargin; // right margin
+var $bMargin; // page break margin
+var $cMargin; // cell margin
+var $x, $y; // current position in user unit
+var $lasth; // height of last printed cell
+var $LineWidth; // line width in user unit
+var $fontpath; // path containing fonts
+var $CoreFonts; // array of core font names
+var $fonts; // array of used fonts
+var $FontFiles; // array of font files
+var $diffs; // array of encoding differences
+var $FontFamily; // current font family
+var $FontStyle; // current font style
+var $underline; // underlining flag
+var $CurrentFont; // current font info
+var $FontSizePt; // current font size in points
+var $FontSize; // current font size in user unit
+var $DrawColor; // commands for drawing color
+var $FillColor; // commands for filling color
+var $TextColor; // commands for text color
+var $ColorFlag; // indicates whether fill and text colors are different
+var $ws; // word spacing
+var $images; // array of used images
+var $PageLinks; // array of links in pages
+var $links; // array of internal links
+var $AutoPageBreak; // automatic page breaking
+var $PageBreakTrigger; // threshold used to trigger page breaks
+var $InHeader; // flag set when processing header
+var $InFooter; // flag set when processing footer
+var $ZoomMode; // zoom display mode
+var $LayoutMode; // layout display mode
+var $title; // title
+var $subject; // subject
+var $author; // author
+var $keywords; // keywords
+var $creator; // creator
+var $AliasNbPages; // alias for total number of pages
+var $PDFVersion; // PDF version number
+
+/*******************************************************************************
+* *
+* Public methods *
+* *
+*******************************************************************************/
+function FPDF($orientation='P', $unit='mm', $size='A4')
+{
+ // Some checks
+ $this->_dochecks();
+ // Initialization of properties
+ $this->page = 0;
+ $this->n = 2;
+ $this->buffer = '';
+ $this->pages = array();
+ $this->PageSizes = array();
+ $this->state = 0;
+ $this->fonts = array();
+ $this->FontFiles = array();
+ $this->diffs = array();
+ $this->images = array();
+ $this->links = array();
+ $this->InHeader = false;
+ $this->InFooter = false;
+ $this->lasth = 0;
+ $this->FontFamily = '';
+ $this->FontStyle = '';
+ $this->FontSizePt = 12;
+ $this->underline = false;
+ $this->DrawColor = '0 G';
+ $this->FillColor = '0 g';
+ $this->TextColor = '0 g';
+ $this->ColorFlag = false;
+ $this->ws = 0;
+ // Font path
+ if(defined('FPDF_FONTPATH'))
+ {
+ $this->fontpath = FPDF_FONTPATH;
+ if(substr($this->fontpath,-1)!='/' && substr($this->fontpath,-1)!='\\')
+ $this->fontpath .= '/';
+ }
+ elseif(is_dir(dirname(__FILE__).'/font'))
+ $this->fontpath = dirname(__FILE__).'/font/';
+ else
+ $this->fontpath = '';
+ // Core fonts
+ $this->CoreFonts = array('courier', 'helvetica', 'times', 'symbol', 'zapfdingbats');
+ // Scale factor
+ if($unit=='pt')
+ $this->k = 1;
+ elseif($unit=='mm')
+ $this->k = 72/25.4;
+ elseif($unit=='cm')
+ $this->k = 72/2.54;
+ elseif($unit=='in')
+ $this->k = 72;
+ else
+ $this->Error('Incorrect unit: '.$unit);
+ // Page sizes
+ $this->StdPageSizes = array('a3'=>array(841.89,1190.55), 'a4'=>array(595.28,841.89), 'a5'=>array(420.94,595.28),
+ 'letter'=>array(612,792), 'legal'=>array(612,1008));
+ $size = $this->_getpagesize($size);
+ $this->DefPageSize = $size;
+ $this->CurPageSize = $size;
+ // Page orientation
+ $orientation = strtolower($orientation);
+ if($orientation=='p' || $orientation=='portrait')
+ {
+ $this->DefOrientation = 'P';
+ $this->w = $size[0];
+ $this->h = $size[1];
+ }
+ elseif($orientation=='l' || $orientation=='landscape')
+ {
+ $this->DefOrientation = 'L';
+ $this->w = $size[1];
+ $this->h = $size[0];
+ }
+ else
+ $this->Error('Incorrect orientation: '.$orientation);
+ $this->CurOrientation = $this->DefOrientation;
+ $this->wPt = $this->w*$this->k;
+ $this->hPt = $this->h*$this->k;
+ // Page margins (1 cm)
+ $margin = 28.35/$this->k;
+ $this->SetMargins($margin,$margin);
+ // Interior cell margin (1 mm)
+ $this->cMargin = $margin/10;
+ // Line width (0.2 mm)
+ $this->LineWidth = .567/$this->k;
+ // Automatic page break
+ $this->SetAutoPageBreak(true,2*$margin);
+ // Default display mode
+ $this->SetDisplayMode('default');
+ // Enable compression
+ $this->SetCompression(true);
+ // Set default PDF version number
+ $this->PDFVersion = '1.3';
+}
+
+function SetMargins($left, $top, $right=null)
+{
+ // Set left, top and right margins
+ $this->lMargin = $left;
+ $this->tMargin = $top;
+ if($right===null)
+ $right = $left;
+ $this->rMargin = $right;
+}
+
+function SetLeftMargin($margin)
+{
+ // Set left margin
+ $this->lMargin = $margin;
+ if($this->page>0 && $this->x<$margin)
+ $this->x = $margin;
+}
+
+function SetTopMargin($margin)
+{
+ // Set top margin
+ $this->tMargin = $margin;
+}
+
+function SetRightMargin($margin)
+{
+ // Set right margin
+ $this->rMargin = $margin;
+}
+
+function SetAutoPageBreak($auto, $margin=0)
+{
+ // Set auto page break mode and triggering margin
+ $this->AutoPageBreak = $auto;
+ $this->bMargin = $margin;
+ $this->PageBreakTrigger = $this->h-$margin;
+}
+
+function SetDisplayMode($zoom, $layout='default')
+{
+ // Set display mode in viewer
+ if($zoom=='fullpage' || $zoom=='fullwidth' || $zoom=='real' || $zoom=='default' || !is_string($zoom))
+ $this->ZoomMode = $zoom;
+ else
+ $this->Error('Incorrect zoom display mode: '.$zoom);
+ if($layout=='single' || $layout=='continuous' || $layout=='two' || $layout=='default')
+ $this->LayoutMode = $layout;
+ else
+ $this->Error('Incorrect layout display mode: '.$layout);
+}
+
+function SetCompression($compress)
+{
+ // Set page compression
+ if(function_exists('gzcompress'))
+ $this->compress = $compress;
+ else
+ $this->compress = false;
+}
+
+function SetTitle($title, $isUTF8=false)
+{
+ // Title of document
+ if($isUTF8)
+ $title = $this->_UTF8toUTF16($title);
+ $this->title = $title;
+}
+
+function SetSubject($subject, $isUTF8=false)
+{
+ // Subject of document
+ if($isUTF8)
+ $subject = $this->_UTF8toUTF16($subject);
+ $this->subject = $subject;
+}
+
+function SetAuthor($author, $isUTF8=false)
+{
+ // Author of document
+ if($isUTF8)
+ $author = $this->_UTF8toUTF16($author);
+ $this->author = $author;
+}
+
+function SetKeywords($keywords, $isUTF8=false)
+{
+ // Keywords of document
+ if($isUTF8)
+ $keywords = $this->_UTF8toUTF16($keywords);
+ $this->keywords = $keywords;
+}
+
+function SetCreator($creator, $isUTF8=false)
+{
+ // Creator of document
+ if($isUTF8)
+ $creator = $this->_UTF8toUTF16($creator);
+ $this->creator = $creator;
+}
+
+function AliasNbPages($alias='{nb}')
+{
+ // Define an alias for total number of pages
+ $this->AliasNbPages = $alias;
+}
+
+function Error($msg)
+{
+ // Fatal error
+ die('<b>FPDF error:</b> '.$msg);
+}
+
+function Open()
+{
+ // Begin document
+ $this->state = 1;
+}
+
+function Close()
+{
+ // Terminate document
+ if($this->state==3)
+ return;
+ if($this->page==0)
+ $this->AddPage();
+ // Page footer
+ $this->InFooter = true;
+ $this->Footer();
+ $this->InFooter = false;
+ // Close page
+ $this->_endpage();
+ // Close document
+ $this->_enddoc();
+}
+
+function AddPage($orientation='', $size='')
+{
+ // Start a new page
+ if($this->state==0)
+ $this->Open();
+ $family = $this->FontFamily;
+ $style = $this->FontStyle.($this->underline ? 'U' : '');
+ $fontsize = $this->FontSizePt;
+ $lw = $this->LineWidth;
+ $dc = $this->DrawColor;
+ $fc = $this->FillColor;
+ $tc = $this->TextColor;
+ $cf = $this->ColorFlag;
+ if($this->page>0)
+ {
+ // Page footer
+ $this->InFooter = true;
+ $this->Footer();
+ $this->InFooter = false;
+ // Close page
+ $this->_endpage();
+ }
+ // Start new page
+ $this->_beginpage($orientation,$size);
+ // Set line cap style to square
+ $this->_out('2 J');
+ // Set line width
+ $this->LineWidth = $lw;
+ $this->_out(sprintf('%.2F w',$lw*$this->k));
+ // Set font
+ if($family)
+ $this->SetFont($family,$style,$fontsize);
+ // Set colors
+ $this->DrawColor = $dc;
+ if($dc!='0 G')
+ $this->_out($dc);
+ $this->FillColor = $fc;
+ if($fc!='0 g')
+ $this->_out($fc);
+ $this->TextColor = $tc;
+ $this->ColorFlag = $cf;
+ // Page header
+ $this->InHeader = true;
+ $this->Header();
+ $this->InHeader = false;
+ // Restore line width
+ if($this->LineWidth!=$lw)
+ {
+ $this->LineWidth = $lw;
+ $this->_out(sprintf('%.2F w',$lw*$this->k));
+ }
+ // Restore font
+ if($family)
+ $this->SetFont($family,$style,$fontsize);
+ // Restore colors
+ if($this->DrawColor!=$dc)
+ {
+ $this->DrawColor = $dc;
+ $this->_out($dc);
+ }
+ if($this->FillColor!=$fc)
+ {
+ $this->FillColor = $fc;
+ $this->_out($fc);
+ }
+ $this->TextColor = $tc;
+ $this->ColorFlag = $cf;
+}
+
+function Header()
+{
+ // To be implemented in your own inherited class
+}
+
+function Footer()
+{
+ // To be implemented in your own inherited class
+}
+
+function PageNo()
+{
+ // Get current page number
+ return $this->page;
+}
+
+function SetDrawColor($r, $g=null, $b=null)
+{
+ // Set color for all stroking operations
+ if(($r==0 && $g==0 && $b==0) || $g===null)
+ $this->DrawColor = sprintf('%.3F G',$r/255);
+ else
+ $this->DrawColor = sprintf('%.3F %.3F %.3F RG',$r/255,$g/255,$b/255);
+ if($this->page>0)
+ $this->_out($this->DrawColor);
+}
+
+function SetFillColor($r, $g=null, $b=null)
+{
+ // Set color for all filling operations
+ if(($r==0 && $g==0 && $b==0) || $g===null)
+ $this->FillColor = sprintf('%.3F g',$r/255);
+ else
+ $this->FillColor = sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255);
+ $this->ColorFlag = ($this->FillColor!=$this->TextColor);
+ if($this->page>0)
+ $this->_out($this->FillColor);
+}
+
+function SetTextColor($r, $g=null, $b=null)
+{
+ // Set color for text
+ if(($r==0 && $g==0 && $b==0) || $g===null)
+ $this->TextColor = sprintf('%.3F g',$r/255);
+ else
+ $this->TextColor = sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255);
+ $this->ColorFlag = ($this->FillColor!=$this->TextColor);
+}
+
+function GetStringWidth($s)
+{
+ // Get width of a string in the current font
+ $s = (string)$s;
+ $cw = &$this->CurrentFont['cw'];
+ $w = 0;
+ $l = strlen($s);
+ for($i=0;$i<$l;$i++)
+ $w += $cw[$s[$i]];
+ return $w*$this->FontSize/1000;
+}
+
+function SetLineWidth($width)
+{
+ // Set line width
+ $this->LineWidth = $width;
+ if($this->page>0)
+ $this->_out(sprintf('%.2F w',$width*$this->k));
+}
+
+function Line($x1, $y1, $x2, $y2)
+{
+ // Draw a line
+ $this->_out(sprintf('%.2F %.2F m %.2F %.2F l S',$x1*$this->k,($this->h-$y1)*$this->k,$x2*$this->k,($this->h-$y2)*$this->k));
+}
+
+function Rect($x, $y, $w, $h, $style='')
+{
+ // Draw a rectangle
+ if($style=='F')
+ $op = 'f';
+ elseif($style=='FD' || $style=='DF')
+ $op = 'B';
+ else
+ $op = 'S';
+ $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op));
+}
+
+function AddFont($family, $style='', $file='')
+{
+ // Add a TrueType, OpenType or Type1 font
+ $family = strtolower($family);
+ if($file=='')
+ $file = str_replace(' ','',$family).strtolower($style).'.php';
+ $style = strtoupper($style);
+ if($style=='IB')
+ $style = 'BI';
+ $fontkey = $family.$style;
+ if(isset($this->fonts[$fontkey]))
+ return;
+ $info = $this->_loadfont($file);
+ $info['i'] = count($this->fonts)+1;
+ if(!empty($info['diff']))
+ {
+ // Search existing encodings
+ $n = array_search($info['diff'],$this->diffs);
+ if(!$n)
+ {
+ $n = count($this->diffs)+1;
+ $this->diffs[$n] = $info['diff'];
+ }
+ $info['diffn'] = $n;
+ }
+ if(!empty($info['file']))
+ {
+ // Embedded font
+ if($info['type']=='TrueType')
+ $this->FontFiles[$info['file']] = array('length1'=>$info['originalsize']);
+ else
+ $this->FontFiles[$info['file']] = array('length1'=>$info['size1'], 'length2'=>$info['size2']);
+ }
+ $this->fonts[$fontkey] = $info;
+}
+
+function SetFont($family, $style='', $size=0)
+{
+ // Select a font; size given in points
+ if($family=='')
+ $family = $this->FontFamily;
+ else
+ $family = strtolower($family);
+ $style = strtoupper($style);
+ if(strpos($style,'U')!==false)
+ {
+ $this->underline = true;
+ $style = str_replace('U','',$style);
+ }
+ else
+ $this->underline = false;
+ if($style=='IB')
+ $style = 'BI';
+ if($size==0)
+ $size = $this->FontSizePt;
+ // Test if font is already selected
+ if($this->FontFamily==$family && $this->FontStyle==$style && $this->FontSizePt==$size)
+ return;
+ // Test if font is already loaded
+ $fontkey = $family.$style;
+ if(!isset($this->fonts[$fontkey]))
+ {
+ // Test if one of the core fonts
+ if($family=='arial')
+ $family = 'helvetica';
+ if(in_array($family,$this->CoreFonts))
+ {
+ if($family=='symbol' || $family=='zapfdingbats')
+ $style = '';
+ $fontkey = $family.$style;
+ if(!isset($this->fonts[$fontkey]))
+ $this->AddFont($family,$style);
+ }
+ else
+ $this->Error('Undefined font: '.$family.' '.$style);
+ }
+ // Select it
+ $this->FontFamily = $family;
+ $this->FontStyle = $style;
+ $this->FontSizePt = $size;
+ $this->FontSize = $size/$this->k;
+ $this->CurrentFont = &$this->fonts[$fontkey];
+ if($this->page>0)
+ $this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt));
+}
+
+function SetFontSize($size)
+{
+ // Set font size in points
+ if($this->FontSizePt==$size)
+ return;
+ $this->FontSizePt = $size;
+ $this->FontSize = $size/$this->k;
+ if($this->page>0)
+ $this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt));
+}
+
+function AddLink()
+{
+ // Create a new internal link
+ $n = count($this->links)+1;
+ $this->links[$n] = array(0, 0);
+ return $n;
+}
+
+function SetLink($link, $y=0, $page=-1)
+{
+ // Set destination of internal link
+ if($y==-1)
+ $y = $this->y;
+ if($page==-1)
+ $page = $this->page;
+ $this->links[$link] = array($page, $y);
+}
+
+function Link($x, $y, $w, $h, $link)
+{
+ // Put a link on the page
+ $this->PageLinks[$this->page][] = array($x*$this->k, $this->hPt-$y*$this->k, $w*$this->k, $h*$this->k, $link);
+}
+
+function Text($x, $y, $txt)
+{
+ // Output a string
+ $s = sprintf('BT %.2F %.2F Td (%s) Tj ET',$x*$this->k,($this->h-$y)*$this->k,$this->_escape($txt));
+ if($this->underline && $txt!='')
+ $s .= ' '.$this->_dounderline($x,$y,$txt);
+ if($this->ColorFlag)
+ $s = 'q '.$this->TextColor.' '.$s.' Q';
+ $this->_out($s);
+}
+
+function AcceptPageBreak()
+{
+ // Accept automatic page break or not
+ return $this->AutoPageBreak;
+}
+
+function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='')
+{
+ // Output a cell
+ $k = $this->k;
+ if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak())
+ {
+ // Automatic page break
+ $x = $this->x;
+ $ws = $this->ws;
+ if($ws>0)
+ {
+ $this->ws = 0;
+ $this->_out('0 Tw');
+ }
+ $this->AddPage($this->CurOrientation,$this->CurPageSize);
+ $this->x = $x;
+ if($ws>0)
+ {
+ $this->ws = $ws;
+ $this->_out(sprintf('%.3F Tw',$ws*$k));
+ }
+ }
+ if($w==0)
+ $w = $this->w-$this->rMargin-$this->x;
+ $s = '';
+ if($fill || $border==1)
+ {
+ if($fill)
+ $op = ($border==1) ? 'B' : 'f';
+ else
+ $op = 'S';
+ $s = sprintf('%.2F %.2F %.2F %.2F re %s ',$this->x*$k,($this->h-$this->y)*$k,$w*$k,-$h*$k,$op);
+ }
+ if(is_string($border))
+ {
+ $x = $this->x;
+ $y = $this->y;
+ if(strpos($border,'L')!==false)
+ $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,$x*$k,($this->h-($y+$h))*$k);
+ if(strpos($border,'T')!==false)
+ $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-$y)*$k);
+ if(strpos($border,'R')!==false)
+ $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',($x+$w)*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-($y+$h))*$k);
+ if(strpos($border,'B')!==false)
+ $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-($y+$h))*$k,($x+$w)*$k,($this->h-($y+$h))*$k);
+ }
+ if($txt!=='')
+ {
+ if($align=='R')
+ $dx = $w-$this->cMargin-$this->GetStringWidth($txt);
+ elseif($align=='C')
+ $dx = ($w-$this->GetStringWidth($txt))/2;
+ else
+ $dx = $this->cMargin;
+ if($this->ColorFlag)
+ $s .= 'q '.$this->TextColor.' ';
+ $txt2 = str_replace(')','\\)',str_replace('(','\\(',str_replace('\\','\\\\',$txt)));
+ $s .= sprintf('BT %.2F %.2F Td (%s) Tj ET',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k,$txt2);
+ if($this->underline)
+ $s .= ' '.$this->_dounderline($this->x+$dx,$this->y+.5*$h+.3*$this->FontSize,$txt);
+ if($this->ColorFlag)
+ $s .= ' Q';
+ if($link)
+ $this->Link($this->x+$dx,$this->y+.5*$h-.5*$this->FontSize,$this->GetStringWidth($txt),$this->FontSize,$link);
+ }
+ if($s)
+ $this->_out($s);
+ $this->lasth = $h;
+ if($ln>0)
+ {
+ // Go to next line
+ $this->y += $h;
+ if($ln==1)
+ $this->x = $this->lMargin;
+ }
+ else
+ $this->x += $w;
+}
+
+function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false)
+{
+ // Output text with automatic or explicit line breaks
+ $cw = &$this->CurrentFont['cw'];
+ if($w==0)
+ $w = $this->w-$this->rMargin-$this->x;
+ $wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
+ $s = str_replace("\r",'',$txt);
+ $nb = strlen($s);
+ if($nb>0 && $s[$nb-1]=="\n")
+ $nb--;
+ $b = 0;
+ if($border)
+ {
+ if($border==1)
+ {
+ $border = 'LTRB';
+ $b = 'LRT';
+ $b2 = 'LR';
+ }
+ else
+ {
+ $b2 = '';
+ if(strpos($border,'L')!==false)
+ $b2 .= 'L';
+ if(strpos($border,'R')!==false)
+ $b2 .= 'R';
+ $b = (strpos($border,'T')!==false) ? $b2.'T' : $b2;
+ }
+ }
+ $sep = -1;
+ $i = 0;
+ $j = 0;
+ $l = 0;
+ $ns = 0;
+ $nl = 1;
+ while($i<$nb)
+ {
+ // Get next character
+ $c = $s[$i];
+ if($c=="\n")
+ {
+ // Explicit line break
+ if($this->ws>0)
+ {
+ $this->ws = 0;
+ $this->_out('0 Tw');
+ }
+ $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
+ $i++;
+ $sep = -1;
+ $j = $i;
+ $l = 0;
+ $ns = 0;
+ $nl++;
+ if($border && $nl==2)
+ $b = $b2;
+ continue;
+ }
+ if($c==' ')
+ {
+ $sep = $i;
+ $ls = $l;
+ $ns++;
+ }
+ $l += $cw[$c];
+ if($l>$wmax)
+ {
+ // Automatic line break
+ if($sep==-1)
+ {
+ if($i==$j)
+ $i++;
+ if($this->ws>0)
+ {
+ $this->ws = 0;
+ $this->_out('0 Tw');
+ }
+ $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
+ }
+ else
+ {
+ if($align=='J')
+ {
+ $this->ws = ($ns>1) ? ($wmax-$ls)/1000*$this->FontSize/($ns-1) : 0;
+ $this->_out(sprintf('%.3F Tw',$this->ws*$this->k));
+ }
+ $this->Cell($w,$h,substr($s,$j,$sep-$j),$b,2,$align,$fill);
+ $i = $sep+1;
+ }
+ $sep = -1;
+ $j = $i;
+ $l = 0;
+ $ns = 0;
+ $nl++;
+ if($border && $nl==2)
+ $b = $b2;
+ }
+ else
+ $i++;
+ }
+ // Last chunk
+ if($this->ws>0)
+ {
+ $this->ws = 0;
+ $this->_out('0 Tw');
+ }
+ if($border && strpos($border,'B')!==false)
+ $b .= 'B';
+ $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
+ $this->x = $this->lMargin;
+}
+
+function Write($h, $txt, $link='')
+{
+ // Output text in flowing mode
+ $cw = &$this->CurrentFont['cw'];
+ $w = $this->w-$this->rMargin-$this->x;
+ $wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
+ $s = str_replace("\r",'',$txt);
+ $nb = strlen($s);
+ $sep = -1;
+ $i = 0;
+ $j = 0;
+ $l = 0;
+ $nl = 1;
+ while($i<$nb)
+ {
+ // Get next character
+ $c = $s[$i];
+ if($c=="\n")
+ {
+ // Explicit line break
+ $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link);
+ $i++;
+ $sep = -1;
+ $j = $i;
+ $l = 0;
+ if($nl==1)
+ {
+ $this->x = $this->lMargin;
+ $w = $this->w-$this->rMargin-$this->x;
+ $wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
+ }
+ $nl++;
+ continue;
+ }
+ if($c==' ')
+ $sep = $i;
+ $l += $cw[$c];
+ if($l>$wmax)
+ {
+ // Automatic line break
+ if($sep==-1)
+ {
+ if($this->x>$this->lMargin)
+ {
+ // Move to next line
+ $this->x = $this->lMargin;
+ $this->y += $h;
+ $w = $this->w-$this->rMargin-$this->x;
+ $wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
+ $i++;
+ $nl++;
+ continue;
+ }
+ if($i==$j)
+ $i++;
+ $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link);
+ }
+ else
+ {
+ $this->Cell($w,$h,substr($s,$j,$sep-$j),0,2,'',0,$link);
+ $i = $sep+1;
+ }
+ $sep = -1;
+ $j = $i;
+ $l = 0;
+ if($nl==1)
+ {
+ $this->x = $this->lMargin;
+ $w = $this->w-$this->rMargin-$this->x;
+ $wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
+ }
+ $nl++;
+ }
+ else
+ $i++;
+ }
+ // Last chunk
+ if($i!=$j)
+ $this->Cell($l/1000*$this->FontSize,$h,substr($s,$j),0,0,'',0,$link);
+}
+
+function Ln($h=null)
+{
+ // Line feed; default value is last cell height
+ $this->x = $this->lMargin;
+ if($h===null)
+ $this->y += $this->lasth;
+ else
+ $this->y += $h;
+}
+
+function Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='')
+{
+ // Put an image on the page
+ if(!isset($this->images[$file]))
+ {
+ // First use of this image, get info
+ if($type=='')
+ {
+ $pos = strrpos($file,'.');
+ if(!$pos)
+ $this->Error('Image file has no extension and no type was specified: '.$file);
+ $type = substr($file,$pos+1);
+ }
+ $type = strtolower($type);
+ if($type=='jpeg')
+ $type = 'jpg';
+ $mtd = '_parse'.$type;
+ if(!method_exists($this,$mtd))
+ $this->Error('Unsupported image type: '.$type);
+ $info = $this->$mtd($file);
+ $info['i'] = count($this->images)+1;
+ $this->images[$file] = $info;
+ }
+ else
+ $info = $this->images[$file];
+
+ // Automatic width and height calculation if needed
+ if($w==0 && $h==0)
+ {
+ // Put image at 96 dpi
+ $w = -96;
+ $h = -96;
+ }
+ if($w<0)
+ $w = -$info['w']*72/$w/$this->k;
+ if($h<0)
+ $h = -$info['h']*72/$h/$this->k;
+ if($w==0)
+ $w = $h*$info['w']/$info['h'];
+ if($h==0)
+ $h = $w*$info['h']/$info['w'];
+
+ // Flowing mode
+ if($y===null)
+ {
+ if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak())
+ {
+ // Automatic page break
+ $x2 = $this->x;
+ $this->AddPage($this->CurOrientation,$this->CurPageSize);
+ $this->x = $x2;
+ }
+ $y = $this->y;
+ $this->y += $h;
+ }
+
+ if($x===null)
+ $x = $this->x;
+ $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q',$w*$this->k,$h*$this->k,$x*$this->k,($this->h-($y+$h))*$this->k,$info['i']));
+ if($link)
+ $this->Link($x,$y,$w,$h,$link);
+}
+
+function GetX()
+{
+ // Get x position
+ return $this->x;
+}
+
+function SetX($x)
+{
+ // Set x position
+ if($x>=0)
+ $this->x = $x;
+ else
+ $this->x = $this->w+$x;
+}
+
+function GetY()
+{
+ // Get y position
+ return $this->y;
+}
+
+function SetY($y)
+{
+ // Set y position and reset x
+ $this->x = $this->lMargin;
+ if($y>=0)
+ $this->y = $y;
+ else
+ $this->y = $this->h+$y;
+}
+
+function SetXY($x, $y)
+{
+ // Set x and y positions
+ $this->SetY($y);
+ $this->SetX($x);
+}
+
+function Output($name='', $dest='')
+{
+ // Output PDF to some destination
+ if($this->state<3)
+ $this->Close();
+ $dest = strtoupper($dest);
+ if($dest=='')
+ {
+ if($name=='')
+ {
+ $name = 'doc.pdf';
+ $dest = 'I';
+ }
+ else
+ $dest = 'F';
+ }
+ switch($dest)
+ {
+ case 'I':
+ // Send to standard output
+ $this->_checkoutput();
+ if(PHP_SAPI!='cli')
+ {
+ // We send to a browser
+ header('Content-Type: application/pdf');
+ header('Content-Disposition: inline; filename="'.$name.'"');
+ header('Cache-Control: private, max-age=0, must-revalidate');
+ header('Pragma: public');
+ }
+ echo $this->buffer;
+ break;
+ case 'D':
+ // Download file
+ $this->_checkoutput();
+ header('Content-Type: application/x-download');
+ header('Content-Disposition: attachment; filename="'.$name.'"');
+ header('Cache-Control: private, max-age=0, must-revalidate');
+ header('Pragma: public');
+ echo $this->buffer;
+ break;
+ case 'F':
+ // Save to local file
+ $f = fopen($name,'wb');
+ if(!$f)
+ $this->Error('Unable to create output file: '.$name);
+ fwrite($f,$this->buffer,strlen($this->buffer));
+ fclose($f);
+ break;
+ case 'S':
+ // Return as a string
+ return $this->buffer;
+ default:
+ $this->Error('Incorrect output destination: '.$dest);
+ }
+ return '';
+}
+
+/*******************************************************************************
+* *
+* Protected methods *
+* *
+*******************************************************************************/
+function _dochecks()
+{
+ // Check availability of %F
+ if(sprintf('%.1F',1.0)!='1.0')
+ $this->Error('This version of PHP is not supported');
+ // Check mbstring overloading
+ if(ini_get('mbstring.func_overload') & 2)
+ $this->Error('mbstring overloading must be disabled');
+ // Ensure runtime magic quotes are disabled
+ if(get_magic_quotes_runtime())
+ @set_magic_quotes_runtime(0);
+}
+
+function _checkoutput()
+{
+ if(PHP_SAPI!='cli')
+ {
+ if(headers_sent($file,$line))
+ $this->Error("Some data has already been output, can't send PDF file (output started at $file:$line)");
+ }
+ if(ob_get_length())
+ {
+ // The output buffer is not empty
+ if(preg_match('/^(\xEF\xBB\xBF)?\s*$/',ob_get_contents()))
+ {
+ // It contains only a UTF-8 BOM and/or whitespace, let's clean it
+ ob_clean();
+ }
+ else
+ $this->Error("Some data has already been output, can't send PDF file");
+ }
+}
+
+function _getpagesize($size)
+{
+ if(is_string($size))
+ {
+ $size = strtolower($size);
+ if(!isset($this->StdPageSizes[$size]))
+ $this->Error('Unknown page size: '.$size);
+ $a = $this->StdPageSizes[$size];
+ return array($a[0]/$this->k, $a[1]/$this->k);
+ }
+ else
+ {
+ if($size[0]>$size[1])
+ return array($size[1], $size[0]);
+ else
+ return $size;
+ }
+}
+
+function _beginpage($orientation, $size)
+{
+ $this->page++;
+ $this->pages[$this->page] = '';
+ $this->state = 2;
+ $this->x = $this->lMargin;
+ $this->y = $this->tMargin;
+ $this->FontFamily = '';
+ // Check page size and orientation
+ if($orientation=='')
+ $orientation = $this->DefOrientation;
+ else
+ $orientation = strtoupper($orientation[0]);
+ if($size=='')
+ $size = $this->DefPageSize;
+ else
+ $size = $this->_getpagesize($size);
+ if($orientation!=$this->CurOrientation || $size[0]!=$this->CurPageSize[0] || $size[1]!=$this->CurPageSize[1])
+ {
+ // New size or orientation
+ if($orientation=='P')
+ {
+ $this->w = $size[0];
+ $this->h = $size[1];
+ }
+ else
+ {
+ $this->w = $size[1];
+ $this->h = $size[0];
+ }
+ $this->wPt = $this->w*$this->k;
+ $this->hPt = $this->h*$this->k;
+ $this->PageBreakTrigger = $this->h-$this->bMargin;
+ $this->CurOrientation = $orientation;
+ $this->CurPageSize = $size;
+ }
+ if($orientation!=$this->DefOrientation || $size[0]!=$this->DefPageSize[0] || $size[1]!=$this->DefPageSize[1])
+ $this->PageSizes[$this->page] = array($this->wPt, $this->hPt);
+}
+
+function _endpage()
+{
+ $this->state = 1;
+}
+
+function _loadfont($font)
+{
+ // Load a font definition file from the font directory
+ include($this->fontpath.$font);
+ $a = get_defined_vars();
+ if(!isset($a['name']))
+ $this->Error('Could not include font definition file');
+ return $a;
+}
+
+function _escape($s)
+{
+ // Escape special characters in strings
+ $s = str_replace('\\','\\\\',$s);
+ $s = str_replace('(','\\(',$s);
+ $s = str_replace(')','\\)',$s);
+ $s = str_replace("\r",'\\r',$s);
+ return $s;
+}
+
+function _textstring($s)
+{
+ // Format a text string
+ return '('.$this->_escape($s).')';
+}
+
+function _UTF8toUTF16($s)
+{
+ // Convert UTF-8 to UTF-16BE with BOM
+ $res = "\xFE\xFF";
+ $nb = strlen($s);
+ $i = 0;
+ while($i<$nb)
+ {
+ $c1 = ord($s[$i++]);
+ if($c1>=224)
+ {
+ // 3-byte character
+ $c2 = ord($s[$i++]);
+ $c3 = ord($s[$i++]);
+ $res .= chr((($c1 & 0x0F)<<4) + (($c2 & 0x3C)>>2));
+ $res .= chr((($c2 & 0x03)<<6) + ($c3 & 0x3F));
+ }
+ elseif($c1>=192)
+ {
+ // 2-byte character
+ $c2 = ord($s[$i++]);
+ $res .= chr(($c1 & 0x1C)>>2);
+ $res .= chr((($c1 & 0x03)<<6) + ($c2 & 0x3F));
+ }
+ else
+ {
+ // Single-byte character
+ $res .= "\0".chr($c1);
+ }
+ }
+ return $res;
+}
+
+function _dounderline($x, $y, $txt)
+{
+ // Underline text
+ $up = $this->CurrentFont['up'];
+ $ut = $this->CurrentFont['ut'];
+ $w = $this->GetStringWidth($txt)+$this->ws*substr_count($txt,' ');
+ return sprintf('%.2F %.2F %.2F %.2F re f',$x*$this->k,($this->h-($y-$up/1000*$this->FontSize))*$this->k,$w*$this->k,-$ut/1000*$this->FontSizePt);
+}
+
+function _parsejpg($file)
+{
+ // Extract info from a JPEG file
+ $a = getimagesize($file);
+ if(!$a)
+ $this->Error('Missing or incorrect image file: '.$file);
+ if($a[2]!=2)
+ $this->Error('Not a JPEG file: '.$file);
+ if(!isset($a['channels']) || $a['channels']==3)
+ $colspace = 'DeviceRGB';
+ elseif($a['channels']==4)
+ $colspace = 'DeviceCMYK';
+ else
+ $colspace = 'DeviceGray';
+ $bpc = isset($a['bits']) ? $a['bits'] : 8;
+ $data = file_get_contents($file);
+ return array('w'=>$a[0], 'h'=>$a[1], 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'DCTDecode', 'data'=>$data);
+}
+
+function _parsepng($file)
+{
+ // Extract info from a PNG file
+ $f = fopen($file,'rb');
+ if(!$f)
+ $this->Error('Can\'t open image file: '.$file);
+ $info = $this->_parsepngstream($f,$file);
+ fclose($f);
+ return $info;
+}
+
+function _parsepngstream($f, $file)
+{
+ // Check signature
+ if($this->_readstream($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10))
+ $this->Error('Not a PNG file: '.$file);
+
+ // Read header chunk
+ $this->_readstream($f,4);
+ if($this->_readstream($f,4)!='IHDR')
+ $this->Error('Incorrect PNG file: '.$file);
+ $w = $this->_readint($f);
+ $h = $this->_readint($f);
+ $bpc = ord($this->_readstream($f,1));
+ if($bpc>8)
+ $this->Error('16-bit depth not supported: '.$file);
+ $ct = ord($this->_readstream($f,1));
+ if($ct==0 || $ct==4)
+ $colspace = 'DeviceGray';
+ elseif($ct==2 || $ct==6)
+ $colspace = 'DeviceRGB';
+ elseif($ct==3)
+ $colspace = 'Indexed';
+ else
+ $this->Error('Unknown color type: '.$file);
+ if(ord($this->_readstream($f,1))!=0)
+ $this->Error('Unknown compression method: '.$file);
+ if(ord($this->_readstream($f,1))!=0)
+ $this->Error('Unknown filter method: '.$file);
+ if(ord($this->_readstream($f,1))!=0)
+ $this->Error('Interlacing not supported: '.$file);
+ $this->_readstream($f,4);
+ $dp = '/Predictor 15 /Colors '.($colspace=='DeviceRGB' ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w;
+
+ // Scan chunks looking for palette, transparency and image data
+ $pal = '';
+ $trns = '';
+ $data = '';
+ do
+ {
+ $n = $this->_readint($f);
+ $type = $this->_readstream($f,4);
+ if($type=='PLTE')
+ {
+ // Read palette
+ $pal = $this->_readstream($f,$n);
+ $this->_readstream($f,4);
+ }
+ elseif($type=='tRNS')
+ {
+ // Read transparency info
+ $t = $this->_readstream($f,$n);
+ if($ct==0)
+ $trns = array(ord(substr($t,1,1)));
+ elseif($ct==2)
+ $trns = array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1)));
+ else
+ {
+ $pos = strpos($t,chr(0));
+ if($pos!==false)
+ $trns = array($pos);
+ }
+ $this->_readstream($f,4);
+ }
+ elseif($type=='IDAT')
+ {
+ // Read image data block
+ $data .= $this->_readstream($f,$n);
+ $this->_readstream($f,4);
+ }
+ elseif($type=='IEND')
+ break;
+ else
+ $this->_readstream($f,$n+4);
+ }
+ while($n);
+
+ if($colspace=='Indexed' && empty($pal))
+ $this->Error('Missing palette in '.$file);
+ $info = array('w'=>$w, 'h'=>$h, 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'FlateDecode', 'dp'=>$dp, 'pal'=>$pal, 'trns'=>$trns);
+ if($ct>=4)
+ {
+ // Extract alpha channel
+ if(!function_exists('gzuncompress'))
+ $this->Error('Zlib not available, can\'t handle alpha channel: '.$file);
+ $data = gzuncompress($data);
+ $color = '';
+ $alpha = '';
+ if($ct==4)
+ {
+ // Gray image
+ $len = 2*$w;
+ for($i=0;$i<$h;$i++)
+ {
+ $pos = (1+$len)*$i;
+ $color .= $data[$pos];
+ $alpha .= $data[$pos];
+ $line = substr($data,$pos+1,$len);
+ $color .= preg_replace('/(.)./s','$1',$line);
+ $alpha .= preg_replace('/.(.)/s','$1',$line);
+ }
+ }
+ else
+ {
+ // RGB image
+ $len = 4*$w;
+ for($i=0;$i<$h;$i++)
+ {
+ $pos = (1+$len)*$i;
+ $color .= $data[$pos];
+ $alpha .= $data[$pos];
+ $line = substr($data,$pos+1,$len);
+ $color .= preg_replace('/(.{3})./s','$1',$line);
+ $alpha .= preg_replace('/.{3}(.)/s','$1',$line);
+ }
+ }
+ unset($data);
+ $data = gzcompress($color);
+ $info['smask'] = gzcompress($alpha);
+ if($this->PDFVersion<'1.4')
+ $this->PDFVersion = '1.4';
+ }
+ $info['data'] = $data;
+ return $info;
+}
+
+function _readstream($f, $n)
+{
+ // Read n bytes from stream
+ $res = '';
+ while($n>0 && !feof($f))
+ {
+ $s = fread($f,$n);
+ if($s===false)
+ $this->Error('Error while reading stream');
+ $n -= strlen($s);
+ $res .= $s;
+ }
+ if($n>0)
+ $this->Error('Unexpected end of stream');
+ return $res;
+}
+
+function _readint($f)
+{
+ // Read a 4-byte integer from stream
+ $a = unpack('Ni',$this->_readstream($f,4));
+ return $a['i'];
+}
+
+function _parsegif($file)
+{
+ // Extract info from a GIF file (via PNG conversion)
+ if(!function_exists('imagepng'))
+ $this->Error('GD extension is required for GIF support');
+ if(!function_exists('imagecreatefromgif'))
+ $this->Error('GD has no GIF read support');
+ $im = imagecreatefromgif($file);
+ if(!$im)
+ $this->Error('Missing or incorrect image file: '.$file);
+ imageinterlace($im,0);
+ $f = @fopen('php://temp','rb+');
+ if($f)
+ {
+ // Perform conversion in memory
+ ob_start();
+ imagepng($im);
+ $data = ob_get_clean();
+ imagedestroy($im);
+ fwrite($f,$data);
+ rewind($f);
+ $info = $this->_parsepngstream($f,$file);
+ fclose($f);
+ }
+ else
+ {
+ // Use temporary file
+ $tmp = tempnam('.','gif');
+ if(!$tmp)
+ $this->Error('Unable to create a temporary file');
+ if(!imagepng($im,$tmp))
+ $this->Error('Error while saving to temporary file');
+ imagedestroy($im);
+ $info = $this->_parsepng($tmp);
+ unlink($tmp);
+ }
+ return $info;
+}
+
+function _newobj()
+{
+ // Begin a new object
+ $this->n++;
+ $this->offsets[$this->n] = strlen($this->buffer);
+ $this->_out($this->n.' 0 obj');
+}
+
+function _putstream($s)
+{
+ $this->_out('stream');
+ $this->_out($s);
+ $this->_out('endstream');
+}
+
+function _out($s)
+{
+ // Add a line to the document
+ if($this->state==2)
+ $this->pages[$this->page] .= $s."\n";
+ else
+ $this->buffer .= $s."\n";
+}
+
+function _putpages()
+{
+ $nb = $this->page;
+ if(!empty($this->AliasNbPages))
+ {
+ // Replace number of pages
+ for($n=1;$n<=$nb;$n++)
+ $this->pages[$n] = str_replace($this->AliasNbPages,$nb,$this->pages[$n]);
+ }
+ if($this->DefOrientation=='P')
+ {
+ $wPt = $this->DefPageSize[0]*$this->k;
+ $hPt = $this->DefPageSize[1]*$this->k;
+ }
+ else
+ {
+ $wPt = $this->DefPageSize[1]*$this->k;
+ $hPt = $this->DefPageSize[0]*$this->k;
+ }
+ $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
+ for($n=1;$n<=$nb;$n++)
+ {
+ // Page
+ $this->_newobj();
+ $this->_out('<</Type /Page');
+ $this->_out('/Parent 1 0 R');
+ if(isset($this->PageSizes[$n]))
+ $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]',$this->PageSizes[$n][0],$this->PageSizes[$n][1]));
+ $this->_out('/Resources 2 0 R');
+ if(isset($this->PageLinks[$n]))
+ {
+ // Links
+ $annots = '/Annots [';
+ foreach($this->PageLinks[$n] as $pl)
+ {
+ $rect = sprintf('%.2F %.2F %.2F %.2F',$pl[0],$pl[1],$pl[0]+$pl[2],$pl[1]-$pl[3]);
+ $annots .= '<</Type /Annot /Subtype /Link /Rect ['.$rect.'] /Border [0 0 0] ';
+ if(is_string($pl[4]))
+ $annots .= '/A <</S /URI /URI '.$this->_textstring($pl[4]).'>>>>';
+ else
+ {
+ $l = $this->links[$pl[4]];
+ $h = isset($this->PageSizes[$l[0]]) ? $this->PageSizes[$l[0]][1] : $hPt;
+ $annots .= sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]>>',1+2*$l[0],$h-$l[1]*$this->k);
+ }
+ }
+ $this->_out($annots.']');
+ }
+ if($this->PDFVersion>'1.3')
+ $this->_out('/Group <</Type /Group /S /Transparency /CS /DeviceRGB>>');
+ $this->_out('/Contents '.($this->n+1).' 0 R>>');
+ $this->_out('endobj');
+ // Page content
+ $p = ($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n];
+ $this->_newobj();
+ $this->_out('<<'.$filter.'/Length '.strlen($p).'>>');
+ $this->_putstream($p);
+ $this->_out('endobj');
+ }
+ // Pages root
+ $this->offsets[1] = strlen($this->buffer);
+ $this->_out('1 0 obj');
+ $this->_out('<</Type /Pages');
+ $kids = '/Kids [';
+ for($i=0;$i<$nb;$i++)
+ $kids .= (3+2*$i).' 0 R ';
+ $this->_out($kids.']');
+ $this->_out('/Count '.$nb);
+ $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]',$wPt,$hPt));
+ $this->_out('>>');
+ $this->_out('endobj');
+}
+
+function _putfonts()
+{
+ $nf = $this->n;
+ foreach($this->diffs as $diff)
+ {
+ // Encodings
+ $this->_newobj();
+ $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>');
+ $this->_out('endobj');
+ }
+ foreach($this->FontFiles as $file=>$info)
+ {
+ // Font file embedding
+ $this->_newobj();
+ $this->FontFiles[$file]['n'] = $this->n;
+ $font = file_get_contents($this->fontpath.$file,true);
+ if(!$font)
+ $this->Error('Font file not found: '.$file);
+ $compressed = (substr($file,-2)=='.z');
+ if(!$compressed && isset($info['length2']))
+ $font = substr($font,6,$info['length1']).substr($font,6+$info['length1']+6,$info['length2']);
+ $this->_out('<</Length '.strlen($font));
+ if($compressed)
+ $this->_out('/Filter /FlateDecode');
+ $this->_out('/Length1 '.$info['length1']);
+ if(isset($info['length2']))
+ $this->_out('/Length2 '.$info['length2'].' /Length3 0');
+ $this->_out('>>');
+ $this->_putstream($font);
+ $this->_out('endobj');
+ }
+ foreach($this->fonts as $k=>$font)
+ {
+ // Font objects
+ $this->fonts[$k]['n'] = $this->n+1;
+ $type = $font['type'];
+ $name = $font['name'];
+ if($type=='Core')
+ {
+ // Core font
+ $this->_newobj();
+ $this->_out('<</Type /Font');
+ $this->_out('/BaseFont /'.$name);
+ $this->_out('/Subtype /Type1');
+ if($name!='Symbol' && $name!='ZapfDingbats')
+ $this->_out('/Encoding /WinAnsiEncoding');
+ $this->_out('>>');
+ $this->_out('endobj');
+ }
+ elseif($type=='Type1' || $type=='TrueType')
+ {
+ // Additional Type1 or TrueType/OpenType font
+ $this->_newobj();
+ $this->_out('<</Type /Font');
+ $this->_out('/BaseFont /'.$name);
+ $this->_out('/Subtype /'.$type);
+ $this->_out('/FirstChar 32 /LastChar 255');
+ $this->_out('/Widths '.($this->n+1).' 0 R');
+ $this->_out('/FontDescriptor '.($this->n+2).' 0 R');
+ if(isset($font['diffn']))
+ $this->_out('/Encoding '.($nf+$font['diffn']).' 0 R');
+ else
+ $this->_out('/Encoding /WinAnsiEncoding');
+ $this->_out('>>');
+ $this->_out('endobj');
+ // Widths
+ $this->_newobj();
+ $cw = &$font['cw'];
+ $s = '[';
+ for($i=32;$i<=255;$i++)
+ $s .= $cw[chr($i)].' ';
+ $this->_out($s.']');
+ $this->_out('endobj');
+ // Descriptor
+ $this->_newobj();
+ $s = '<</Type /FontDescriptor /FontName /'.$name;
+ foreach($font['desc'] as $k=>$v)
+ $s .= ' /'.$k.' '.$v;
+ if(!empty($font['file']))
+ $s .= ' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
+ $this->_out($s.'>>');
+ $this->_out('endobj');
+ }
+ else
+ {
+ // Allow for additional types
+ $mtd = '_put'.strtolower($type);
+ if(!method_exists($this,$mtd))
+ $this->Error('Unsupported font type: '.$type);
+ $this->$mtd($font);
+ }
+ }
+}
+
+function _putimages()
+{
+ foreach(array_keys($this->images) as $file)
+ {
+ $this->_putimage($this->images[$file]);
+ unset($this->images[$file]['data']);
+ unset($this->images[$file]['smask']);
+ }
+}
+
+function _putimage(&$info)
+{
+ $this->_newobj();
+ $info['n'] = $this->n;
+ $this->_out('<</Type /XObject');
+ $this->_out('/Subtype /Image');
+ $this->_out('/Width '.$info['w']);
+ $this->_out('/Height '.$info['h']);
+ if($info['cs']=='Indexed')
+ $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]');
+ else
+ {
+ $this->_out('/ColorSpace /'.$info['cs']);
+ if($info['cs']=='DeviceCMYK')
+ $this->_out('/Decode [1 0 1 0 1 0 1 0]');
+ }
+ $this->_out('/BitsPerComponent '.$info['bpc']);
+ if(isset($info['f']))
+ $this->_out('/Filter /'.$info['f']);
+ if(isset($info['dp']))
+ $this->_out('/DecodeParms <<'.$info['dp'].'>>');
+ if(isset($info['trns']) && is_array($info['trns']))
+ {
+ $trns = '';
+ for($i=0;$i<count($info['trns']);$i++)
+ $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
+ $this->_out('/Mask ['.$trns.']');
+ }
+ if(isset($info['smask']))
+ $this->_out('/SMask '.($this->n+1).' 0 R');
+ $this->_out('/Length '.strlen($info['data']).'>>');
+ $this->_putstream($info['data']);
+ $this->_out('endobj');
+ // Soft mask
+ if(isset($info['smask']))
+ {
+ $dp = '/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns '.$info['w'];
+ $smask = array('w'=>$info['w'], 'h'=>$info['h'], 'cs'=>'DeviceGray', 'bpc'=>8, 'f'=>$info['f'], 'dp'=>$dp, 'data'=>$info['smask']);
+ $this->_putimage($smask);
+ }
+ // Palette
+ if($info['cs']=='Indexed')
+ {
+ $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
+ $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
+ $this->_newobj();
+ $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
+ $this->_putstream($pal);
+ $this->_out('endobj');
+ }
+}
+
+function _putxobjectdict()
+{
+ foreach($this->images as $image)
+ $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R');
+}
+
+function _putresourcedict()
+{
+ $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
+ $this->_out('/Font <<');
+ foreach($this->fonts as $font)
+ $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
+ $this->_out('>>');
+ $this->_out('/XObject <<');
+ $this->_putxobjectdict();
+ $this->_out('>>');
+}
+
+function _putresources()
+{
+ $this->_putfonts();
+ $this->_putimages();
+ // Resource dictionary
+ $this->offsets[2] = strlen($this->buffer);
+ $this->_out('2 0 obj');
+ $this->_out('<<');
+ $this->_putresourcedict();
+ $this->_out('>>');
+ $this->_out('endobj');
+}
+
+function _putinfo()
+{
+ $this->_out('/Producer '.$this->_textstring('FPDF '.FPDF_VERSION));
+ if(!empty($this->title))
+ $this->_out('/Title '.$this->_textstring($this->title));
+ if(!empty($this->subject))
+ $this->_out('/Subject '.$this->_textstring($this->subject));
+ if(!empty($this->author))
+ $this->_out('/Author '.$this->_textstring($this->author));
+ if(!empty($this->keywords))
+ $this->_out('/Keywords '.$this->_textstring($this->keywords));
+ if(!empty($this->creator))
+ $this->_out('/Creator '.$this->_textstring($this->creator));
+ $this->_out('/CreationDate '.$this->_textstring('D:'.@date('YmdHis')));
+}
+
+function _putcatalog()
+{
+ $this->_out('/Type /Catalog');
+ $this->_out('/Pages 1 0 R');
+ if($this->ZoomMode=='fullpage')
+ $this->_out('/OpenAction [3 0 R /Fit]');
+ elseif($this->ZoomMode=='fullwidth')
+ $this->_out('/OpenAction [3 0 R /FitH null]');
+ elseif($this->ZoomMode=='real')
+ $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
+ elseif(!is_string($this->ZoomMode))
+ $this->_out('/OpenAction [3 0 R /XYZ null null '.sprintf('%.2F',$this->ZoomMode/100).']');
+ if($this->LayoutMode=='single')
+ $this->_out('/PageLayout /SinglePage');
+ elseif($this->LayoutMode=='continuous')
+ $this->_out('/PageLayout /OneColumn');
+ elseif($this->LayoutMode=='two')
+ $this->_out('/PageLayout /TwoColumnLeft');
+}
+
+function _putheader()
+{
+ $this->_out('%PDF-'.$this->PDFVersion);
+}
+
+function _puttrailer()
+{
+ $this->_out('/Size '.($this->n+1));
+ $this->_out('/Root '.$this->n.' 0 R');
+ $this->_out('/Info '.($this->n-1).' 0 R');
+}
+
+function _enddoc()
+{
+ $this->_putheader();
+ $this->_putpages();
+ $this->_putresources();
+ // Info
+ $this->_newobj();
+ $this->_out('<<');
+ $this->_putinfo();
+ $this->_out('>>');
+ $this->_out('endobj');
+ // Catalog
+ $this->_newobj();
+ $this->_out('<<');
+ $this->_putcatalog();
+ $this->_out('>>');
+ $this->_out('endobj');
+ // Cross-ref
+ $o = strlen($this->buffer);
+ $this->_out('xref');
+ $this->_out('0 '.($this->n+1));
+ $this->_out('0000000000 65535 f ');
+ for($i=1;$i<=$this->n;$i++)
+ $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i]));
+ // Trailer
+ $this->_out('trailer');
+ $this->_out('<<');
+ $this->_puttrailer();
+ $this->_out('>>');
+ $this->_out('startxref');
+ $this->_out($o);
+ $this->_out('%%EOF');
+ $this->state = 3;
+}
+// End of class
+}
+
+// Handle special IE contype request
+if(isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT']=='contype')
+{
+ header('Content-Type: application/pdf');
+ exit;
+}
+
+?>
diff --git a/classes/offer.class.php b/classes/offer.class.php
new file mode 100644
index 0000000..1a465f4
--- /dev/null
+++ b/classes/offer.class.php
@@ -0,0 +1,628 @@
+<?php
+/**
+ * Provides the offer class, an interface to the offer table in the database
+ *
+ * @author Camil Staps
+ *
+ * BusinessAdmin: administrative software for small companies
+ * Copyright (C) 2015 Camil Staps (ViviSoft)
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * An interface to the offer table in the database
+ */
+class offer {
+ /**
+ * @var pdo $pdo The PDO class for database communication
+ * @var int $id The id of the offer
+ * @var int $contactId The id of the contact this offer is linked to
+ * @var int $start_date A UNIX timestamp of the start date
+ * @var int $end_date A UNIX timestamp of the end date
+ * @var int $invoice_date A UNIX timestamp of the invoice date
+ * @var bool $accepted Whether the offer is accepted or not
+ * @var null|int $invoice_fileId If an invoice has been generated, an the id of the file
+ * @var null|int $payment_received A UNIX timestamp of the date the payment has been received
+ */
+ protected $pdo, $id, $contactId, $start_date, $end_date, $invoice_date, $accepted, $invoice_fileId, $payment_received;
+
+ const SUBTOTAL = 1;
+ const VAT = 2;
+ const TOTAL = 3;
+
+ /**
+ * Create a new instance
+ *
+ * @param PDO $pdo The PDO class, to access the database
+ * @param int $id The id of the offer to fetch
+ *
+ * @throws PDOException If something went wrong with the database
+ * @throws Exception If the offer could not be found
+ */
+ public function __construct($pdo, $id) {
+ $this->pdo = $pdo;
+
+ $stmt = $this->pdo->prepare("SELECT * FROM `".constants::db_prefix."offer` WHERE `id`=?");
+ $stmt->execute(array($id));
+ if ($stmt->rowCount() == 0) {
+ throw new Exception("The offer with id '$id' could not be found.");
+ }
+ $offer = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ $this->id = $offer['id'];
+ $this->contactId = $offer['contactId'];
+ $this->start_date = strtotime($offer['start_date']);
+ $this->end_date = strtotime($offer['end_date']);
+ $this->invoice_date = strtotime($offer['invoice_date']);
+ $this->accepted = (bool) $offer['accepted'];
+ $this->invoice_fileId = $offer['invoice_fileId'];
+ $this->payment_received = ($offer['payment_received'] == null) ? null : strtotime($offer['payment_received']);
+ }
+
+ //------------------------------------------------------------------------------
+ // Getters and setters
+ //------------------------------------------------------------------------------
+
+ /**
+ * Get the ID of the offer
+ *
+ * @return int The ID
+ */
+ public function getId() {
+ return $this->id;
+ }
+
+ /**
+ * Get the ID of the contact that this offer is linked to
+ *
+ * @see offer::getContact() This function returns the contact as an instance of the object class
+ *
+ * @return int The ID
+ */
+ public function getContactId() {
+ return $this->contactId;
+ }
+
+ /**
+ * Get the contact that this offer is linked to
+ *
+ * @see offer::getContactId() This function returns just the id
+ *
+ * @return contact The contact
+ */
+ public function getContact() {
+ return new contact($this->pdo, $this->contactId);
+ }
+
+ /**
+ * Get all assignment ids for this offer
+ *
+ * @see offer::getAssignments() This funtion returns instances of the assignment class instead of just the ids
+ *
+ * @throws PDOException Is something went wrong with the database
+ *
+ * @return int[] The ids
+ */
+ public function getAssignmentIds() {
+ $ids = array();
+ $assignments = $this->pdo->query("SELECT `id` FROM `".constants::db_prefix."assignment` WHERE `offerId`={$this->id}")->fetchAll(PDO::FETCH_ASSOC);
+ foreach ($assignments as $assignment) {
+ $ids[] = $assignment['id'];
+ }
+ return $ids;
+ }
+
+ /**
+ * Get all assignments for this offer
+ *
+ * @see offer::getAssignmentIds() This function returns just the ids of the assignments, and not instances of the assignment class
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return assignment[] An array indexed by id of instances of the assignment class
+ */
+ public function getAssignments() {
+ $ids = $this->getAssignmentIds();
+ $assignments = array();
+ foreach ($ids as $id) {
+ $assignments[$id] = new assignment($this->pdo, $id);
+ }
+ return $assignments;
+ }
+
+ /**
+ * Get the start date of the assignment
+ *
+ * @return int The start date as a UNIX timestamp
+ */
+ public function getStartDate() {
+ return $this->start_date;
+ }
+
+ /**
+ * Set the start date of the assignment
+ *
+ * @param int $start_date The new start date for the assignment as a UNIX timestamp
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setStartDate($start_date) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."offer` SET `start_date`=? WHERE `id`=?");
+ $stmt->execute(array(date('Y-m-d', $start_date), $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->start_date = $start_date;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the end date of the assignment
+ *
+ * @return int The end date as a UNIX timestamp
+ */
+ public function getEndDate() {
+ return $this->end_date;
+ }
+
+ /**
+ * Set the end date of the assignment
+ *
+ * @param int $end_date The new end date for the assignment as a UNIX timestamp
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setEndDate($end_date) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."offer` SET `end_date`=? WHERE `id`=?");
+ $stmt->execute(array(date('Y-m-d', $end_date), $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->end_date = $end_date;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the invoice date of the assignment
+ *
+ * @return int The invoice date as a UNIX timestamp
+ */
+ public function getInvoiceDate() {
+ return $this->invoice_date;
+ }
+
+ /**
+ * Set the invoice date of the assignment
+ *
+ * @param int $invoice_date The new invoice date for the assignment as a UNIX timestamp
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setInvoiceDate($invoice_date) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."offer` SET `invoice_date`=? WHERE `id`=?");
+ $stmt->execute(array(date('Y-m-d', $invoice_date), $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->invoice_date = $invoice_date;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the date the payment was received
+ *
+ * @return int|null The date as a UNIX timestamp, or null if it wasn't received yet
+ */
+ public function getPaymentReceived() {
+ return $this->payment_received;
+ }
+
+ /**
+ * Set the payment received date of the assignment
+ *
+ * @param int $payment_received The new date the payment has been received as a UNIX timestamp
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setPaymentReceived($payment_received) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."offer` SET `payment_received`=? WHERE `id`=?");
+ $stmt->execute(array(date('Y-m-d', $payment_received), $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->payment_received = $payment_received;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Check if the offer is accepted or not
+ *
+ * @return bool True if the offer is accepted, false if not
+ */
+ public function isAccepted() {
+ return $this->accepted;
+ }
+
+ /**
+ * Toggle the `accepted' status of the offer
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on success, false on failure
+ */
+ public function toggleAccepted() {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."offer` SET `accepted`=? WHERE `id`=?");
+ $new_value = !$this->accepted;
+ $stmt->execute(array($new_value, $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->accepted = $new_value;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the ID of the file that the invoice of this offer is linked to
+ *
+ * @see offer::getInvoiceFile() This function returns the file as an instance of the file class
+ *
+ * @return int The ID
+ */
+ public function getInvoiceFileId() {
+ return $this->invoice_fileId;
+ }
+
+ /**
+ * Get the file that the invoice this offer is linked to
+ *
+ * @see offer::getInvoiceId() This function returns just the id
+ *
+ * @return file|null The file, or null if it doesn't exist
+ */
+ public function getInvoiceFile() {
+ if ($this->invoice_fileId != null) {
+ return new file($this->pdo, $this->invoice_fileId);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Set the invoice file id of the assignment
+ *
+ * @param int $invoice_fileId The new invoice file id for the assignment
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on succes, false on failure
+ */
+ public function setInvoiceFileId($invoice_fileId) {
+ $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."offer` SET `invoice_fileId`=? WHERE `id`=?");
+ $stmt->execute(array($invoice_fileId, $this->id));
+ if ($stmt->rowCount() == 1) {
+ $this->invoice_fileId = $invoice_fileId;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ //------------------------------------------------------------------------------
+ // Other functions
+ //------------------------------------------------------------------------------
+
+ /**
+ * Calculate a handy number about the invoice
+ *
+ * Subtotal: the sum of the prices of the assignments excl. VAT
+ *
+ * VAT: the sum of all the VAT from all the assignments
+ *
+ * Total: the sum of subtotal and total
+ *
+ * @param int $what Any of offer::SUBTOTAL, offer::VAT and offer::TOTAL
+ * @param int $round How many decimals to round the result on
+ * @param bool $format Whether to format the number nicely (for output) or not (for calculations)
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return float|bool The calculated value rounded to $round decimals, or false on incorrect input
+ */
+ public function calculate($what = self::TOTAL, $round = 2, $format = true) {
+ $return = 0;
+ switch ($what) {
+ case self::SUBTOTAL:
+ $assignments = $this->getAssignments();
+ foreach ($assignments as $assignment) {
+ $return += $assignment->calculate(assignment::SUBTOTAL, $round + 1, false);
+ }
+ break;
+ case self::VAT:
+ $assignments = $this->getAssignments();
+ foreach ($assignments as $assignment) {
+ $return += $assignment->calculate(assignment::VAT, $round + 1, false);
+ }
+ break;
+ case self::TOTAL:
+ $return = $this->calculate(self::SUBTOTAL, $round + 1, false) + $this->calculate(self::VAT, $round + 1, false);
+ break;
+ default:
+ return false;
+ }
+ if ($format) {
+ return number_format($return, 2);
+ } else {
+ return round($return, 2);
+ }
+ }
+
+ /**
+ * Remove this offer from the database
+ *
+ * If this doesn't succeed (i.e. false is returned), that means the offer was removed manually or by another instance of this class
+ *
+ * @throws PDOException If something went wrong with the database
+ *
+ * @return bool True on success, false on failure
+ */
+ public function delete() {
+ $stmt = $this->pdo->prepare("DELETE FROM `".constants::db_prefix."offer` WHERE `id`=?");
+ $stmt->execute(array($this->id));
+ if ($stmt->rowCount() != 1) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Make a new assignment linked to this order
+ *
+ * @param string $title The title for this assignment
+ * @param string $description The description for this assignment
+ * @param int $hours The amount of hours to work on this assignment
+ * @param float $price_per_hour The price per hour on this assignment
+ * @param float $vat The VAT percentage (so, 21 for 21%, not 0.21!)
+ *
+ * @throws PDOException If something went wrong with the database
+ * @throws Exception If there was a problem with the input
+ *
+ * @return assignment A new instance of the assignment class containing the new assignment
+ */
+ public function createAssignment($title, $description, $hours, $price_per_hour, $vat) {
+ $stmt = $this->pdo->prepare("INSERT INTO `".constants::db_prefix."assignment` (`offerId`,`title`,`description`,`hours`,`price_per_hour`,`VAT_percentage`) VALUES (?,?,?,?,?,?)");
+ $stmt->execute(array(
+ $this->id,
+ $title,
+ $description,
+ $hours,
+ $price_per_hour,
+ $vat
+ ));
+ if ($stmt->rowCount() == 1) {
+ return new assignment($this->pdo, $this->pdo->lastInsertId());
+ } else {
+ $error = $stmt->errorInfo();
+ throw new Exception($error[2]);
+ }
+ }
+
+ /**
+ * Generate a PDF invoice
+ *
+ * @throws PDOException If something went wrong with the database
+ * @throws Exception If the file could not be written or an other error occured
+ *
+ * @return file An instance of the file class with information on the invoice file generated
+ *
+ * @todo This function was directly copied from the old admin.vivisoft.nl class invoice. It should be checked and improved.
+ * @todo This function should use self::calculate()
+ */
+ public function generateInvoice() {
+ // Check if we already have a file
+ $file = $this->getInvoiceFile();
+ if (!($file instanceof file)) {
+ // If not, create a new file
+ $i = 1;
+ do {
+ $invoice_nr = date('Y',$this->invoice_date) . str_pad($i++, 2, '0', STR_PAD_LEFT);
+ $filename = 'invoice-' . $invoice_nr . '.pdf';
+ } while (file_exists(constants::files_folder . $filename));
+ $file = BusinessAdmin::createFile($this->pdo, $filename);
+
+ $this->setInvoiceFileId($file->getId());
+ } else {
+ $invoice_nr = str_replace(array('invoice-','.pdf'), array('',''), $file->getFilename());
+ }
+
+ $assignments = $this->getAssignments();
+ $list = array();
+ foreach ($assignments as $assignment)
+ $list[] = array(
+ $assignment->getTitle(),
+ $assignment->getPricePerHour() * $assignment->getHours(),
+ $assignment->getVAT() . "%",
+ $assignment->getPricePerHour() * $assignment->getHours() * (1 + $assignment->getVAT() / 100)
+ );
+
+ $pdf = new correspondence();
+ $pdf->SetContact($this->getContact());
+ $pdf->SetTitle($pdf->_('invoice') . ' ' . $invoice_nr);
+ $pdf->AddPage();
+ $pdf->correspondenceHeader();
+
+ $pdf->SetY(100);
+ $pdf->SetFont('','B',14);
+ $pdf->SetTextColor(correspondence::HEAD_RED, correspondence::HEAD_GREEN, correspondence::HEAD_BLUE);
+ $pdf->Cell(60,6, $pdf->_('invoice'),'B');
+ $pdf->SetTextColor(0);
+ $pdf->Ln();
+
+ $width = array(90,25,20,25);
+ $subtotal = 0;
+ $btw = array();
+ $total = 0;
+
+ // Header
+
+ $pdf->SetFont('','',9);
+ $pdf->Cell(60,4);
+ $pdf->Ln();
+ $pdf->SetFont('','B');
+ $pdf->Cell(30,4.5,$pdf->_('invoice-date'));
+ $pdf->SetFont('','');
+ $pdf->Cell(50,4.5,date("d-m-Y", $this->invoice_date));
+ $pdf->Ln();
+ $pdf->SetFont('','B');
+ $pdf->Cell(30,4.5,$pdf->_('invoice-nr'));
+ $pdf->SetFont('','');
+ $pdf->Cell(50,4.5,$invoice_nr);
+ $pdf->Ln();
+ $pdf->SetFont('','B');
+ $pdf->Cell(30,4.5,$pdf->_('due-date'));
+ $pdf->SetFont('','');
+ $pdf->Cell(50,4.5,date("d-m-Y",$this->invoice_date+3600*24*30));
+ $pdf->Ln();
+ $pdf->Cell(60,4.5,'','B');
+ $pdf->Ln();
+
+ $pdf->SetY(140);
+
+ // Table
+
+ $pdf->SetFont('','B',11);
+ $pdf->SetTextColor(correspondence::HEAD_RED, correspondence::HEAD_GREEN, correspondence::HEAD_BLUE);
+ $pdf->Cell($width[0],7,$pdf->_('description'),'B');
+ $pdf->Cell($width[1],7,$pdf->_('price-excl'),'B',0,'R');
+ $pdf->Cell($width[2],7,$pdf->_('vat'),'B',0,'R');
+ $pdf->Cell($width[3],7,$pdf->_('price-incl'),'B',0,'R');
+ $pdf->SetTextColor(0);
+ $pdf->Ln();
+
+ $pdf->SetFont('','');
+ foreach ($list as $row) {
+ $x = $pdf->getX();
+ $y = $pdf->getY();
+ $pdf->MultiCell($width[0],6,$row[0],0,'L');
+ $newy = $pdf->getY();
+ $pdf->SetXY($x + $width[0], $y);
+ $pdf->Cell($width[1],6,constants::invoice_valuta.number_format($row[1],2),'',0,'R');
+ $pdf->Cell($width[2],6,round($row[2],0) . '%','',0,'R');
+ $pdf->Cell($width[3],6,constants::invoice_valuta.number_format($row[3],2),'',0,'R');
+ $pdf->Ln();
+ $pdf->SetY($newy);
+ $subtotal += $row[1];
+ if (!isset($btw[$row[2]])) $btw[$row[2]] = 0;
+ $btw[$row[2]] += $row[3] - $row[1];
+ }
+ $total = $subtotal;
+ foreach ($btw as $m) {
+ $total += $m;
+ }
+
+ $pdf->Cell(array_sum($width),5,'','T');
+ $pdf->Ln();
+ $pdf->Cell(array_sum($width),5);
+ $pdf->Ln();
+
+ $pdf->Cell($width[0],7);
+ $pdf->SetFont('','B');
+ $pdf->Cell($width[1] + $width[2],7,$pdf->_('amount'));
+ $pdf->SetFont('','');
+ $pdf->Cell($width[3],7,constants::invoice_valuta . $this->calculate(self::SUBTOTAL),'',0,'R');
+ $pdf->Ln();
+
+ foreach ($btw as $p => $m) {
+ $pdf->Cell($width[0],7);
+ $pdf->Cell($width[1] + $width[2],7,$pdf->_('vat') . ' '.round($p,0).'%');
+ $pdf->Cell($width[3],7,constants::invoice_valuta . number_format($m,2),'',0,'R');
+ $pdf->Ln();
+ }
+
+ $pdf->Cell(array_sum($width),5);
+ $pdf->Ln();
+
+ $pdf->Cell($width[0],7);
+ $pdf->SetFont('','B');
+ $pdf->Cell($width[1] + $width[2],7,$pdf->_('total'));
+ $pdf->SetFont('','');
+ $pdf->Cell($width[3],7,constants::invoice_valuta . $this->calculate(self::TOTAL),'T',0,'R');
+ $pdf->Ln();
+
+ // Footer
+
+ $pdf->Ln();
+ if ($pdf->GetY() < 230) {
+ $pdf->SetY(230);
+ }
+ $oldY = $pdf->GetY();
+ $pdf->Cell(45,20,'',1);
+ $pdf->Cell(12.5,20);
+ $pdf->Cell(45,20,'',1);
+ $pdf->Cell(12.5,20);
+ $pdf->Cell(45,20,'',1);
+
+ $pdf->SetFont('','B',10);
+ $pdf->SetTextColor(correspondence::HEAD_RED, correspondence::HEAD_GREEN, correspondence::HEAD_BLUE);
+ $pdf->SetY($oldY + 3);
+ $pdf->Cell(5,5);
+ $pdf->Cell(40,5,$pdf->_('iban'));
+ $pdf->Cell(17.5,5);
+ $pdf->Cell(40,5,$pdf->_('invoice-nr'));
+ $pdf->Cell(17.5,5);
+ $pdf->Cell(40,5,$pdf->_('amount-due'));
+ $pdf->SetTextColor(0);
+
+ $pdf->SetFont('','',8);
+ $pdf->Ln();
+ $pdf->Ln();
+ $oldY = $pdf->GetY();
+
+ $pdf->Cell(5,5);
+ $pdf->Cell(40,5, constants::invoice_iban);
+ $pdf->Cell(17.5,5);
+ $pdf->Cell(40,5,$invoice_nr);
+ $pdf->Cell(17.5,5);
+ $pdf->Cell(40,5,constants::invoice_valuta . $this->calculate(self::TOTAL));
+
+ $pdf->SetY($oldY + 14);
+
+ $pdf->SetFontSize(7);
+ $pdf->Cell(160,5,$pdf->_('request'),0,0,'C');
+ $pdf->Ln();
+ $pdf->Cell(160,5,str_replace('%%', constants::invoice_bic, $pdf->_('biccode')),0,0,'C');
+
+ if (file_exists($file->getFilenamePath())) {
+ unlink($file->getFilenamePath());
+ }
+ $pdf->Output($file->getFilenamePath(),'F');
+ chmod($file->getFilenamePath(),0644);
+
+ return $file;
+ }
+} \ No newline at end of file
diff --git a/classes/response.class.php b/classes/response.class.php
new file mode 100644
index 0000000..887c700
--- /dev/null
+++ b/classes/response.class.php
@@ -0,0 +1,112 @@
+<?php
+/**
+ * Provides the response class
+ *
+ * @author Camil Staps
+ *
+ * BusinessAdmin: administrative software for small companies
+ * Copyright (C) 2015 Camil Staps (ViviSoft)
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Provides a standard to base all responses to be called with AJAX on
+ */
+class response {
+ /** The variable to keep the response in until output */
+ private $response;
+ /** The variable to keep the HTTP response code in until output */
+ private $http_response_code;
+
+ /**
+ * Create a new instance
+ */
+ public function __construct() {
+ $this->response = array();
+ $this->http_response_code = 200;
+ }
+
+ /**
+ * Set a variable of the response
+ *
+ * @param string $name The name of the variable to set
+ * @param string $value The (new) value for the variable
+ */
+ public function __set($name, $value) {
+ $this->response[$name] = $value;
+ }
+
+ /**
+ * Get a variable of the response
+ *
+ * @param string $name The name of the variable to get
+ *
+ * @return mixed The value of the variable
+ */
+ public function __get($name) {
+ return $this->response[$name];
+ }
+
+ /**
+ * Check if a variable of the response is set
+ *
+ * @param string $name The name of the variable to check
+ *
+ * @return bool True if the variable exists, false otherwise
+ */
+ public function __isset($name) {
+ return isset($this->response[$name]);
+ }
+
+ /**
+ * Unset a variable of the response
+ *
+ * @param string $name The variable to unset
+ */
+ public function __unset($name) {
+ unset($this->response[$name]);
+ }
+
+ /**
+ * Get or set the HTTP response code
+ *
+ * If a parameter is provided, it is used as the new HTTP response code, and a bool is returned for success or failure. Otherwise, the current one is returned.
+ *
+ * @param int $code The new code
+ *
+ * @return int|bool True on successful change, false on unsuccesful change, int when the current code is returned
+ */
+ public function http_response_code($code = null) {
+ if ($code === null) {
+ return $this->http_response_code;
+ } else {
+ if (http_response_code($code)) {
+ $this->http_response_code = $code;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Output the response in json
+ *
+ * @return string The response in json format
+ */
+ public function getJson() {
+ return json_encode($this->response);
+ }
+} \ No newline at end of file