diff --git a/.puppet/profiles/icinga2_dev/files/constants.conf b/.puppet/profiles/icinga2_dev/files/constants.conf index 9e6ed51af..fb714b2fb 100644 --- a/.puppet/profiles/icinga2_dev/files/constants.conf +++ b/.puppet/profiles/icinga2_dev/files/constants.conf @@ -6,6 +6,16 @@ /* The directory which contains the plugins from the Monitoring Plugins project. */ const PluginDir = "/usr/lib64/nagios/plugins" +/* The directory which contains the Manubulon plugins. + * Check the documentation, chapter "SNMP Manubulon Plugin Check Commands", for details. + */ +const ManubulonPluginDir = "/usr/lib64/nagios/plugins" + +/* The directory which you use to store additional plugins which ITL provides user contributed command definitions for. + * Check the documentation, chapter "Plugins Contribution", for details. + */ +const PluginContribDir = "/usr/lib64/nagios/plugins" + /* Our local instance name. By default this is the server's hostname as returned by `hostname --fqdn`. * This should be the common name from the API certificate. */ @@ -13,3 +23,4 @@ const NodeName = "localhost" /* Our local zone name. */ const ZoneName = NodeName +const TicketSalt = "" diff --git a/ChangeLog b/ChangeLog index 8954889e5..132c6e0f6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,25 @@ # Icinga Web 2 Changelog +## What's New + +### What's New in Version 2.3.4/2.3.3 + +#### Bugfixes + +* Bug 11267: Links in plugin output don't behave as expected +* Bug 11348: Host aliases are not shown in detail area +* Bug 11728: First non whitespace character after comma stripped from plugin output +* Bug 11729: Sort by severity depends on state type +* Bug 11737: Zero width space characters destroy state highlighting in plugin output +* Bug 11796: Zero width space characters may destroy links in plugin output +* Bug 11831: module.info parsing fails in case it contains newlines that are not part of the module's description +* Bug 11850: "Add to menu" tab unnecessarily appears in command forms +* Bug 11871: Colors used in the timeline are not accessible +* Bug 11883: Delete action on comments and downtimes in list views not accessible because they lack context +* Bug 11885: Database: Asterisk filters ignored when combined w/ other filters +* Bug 11910: Web 2 lacks mobile meta tags +* Fix remote code execution via remote command transport + ### What's New in Version 2.3.2 #### Feature @@ -14,8 +34,6 @@ * Bug 10848: Can't change items per page if filter is in modify state * Bug 11392: Can't configure monitoring backend via the web interface when no monitoring backend was configured -## What's New - ### What's New in Version 2.3.1 #### Bugfixes diff --git a/VERSION b/VERSION index f706a60d8..20dd63223 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.3.2 +v2.3.4 diff --git a/application/controllers/AccountController.php b/application/controllers/AccountController.php new file mode 100644 index 000000000..25bc97756 --- /dev/null +++ b/application/controllers/AccountController.php @@ -0,0 +1,73 @@ +getTabs() + ->add('account', array( + 'title' => $this->translate('Update your account'), + 'label' => $this->translate('My Account'), + 'url' => 'account' + )) + ->add('navigation', array( + 'title' => $this->translate('List and configure your own navigation items'), + 'label' => $this->translate('Navigation'), + 'url' => 'navigation' + )); + } + + /** + * My account + */ + public function indexAction() + { + $config = Config::app()->getSection('global'); + $user = $this->Auth()->getUser(); + if ($user->getAdditional('backend_type') === 'db') { + try { + $userBackend = UserBackend::create($user->getAdditional('backend_name')); + } catch (ConfigurationError $e) { + $userBackend = null; + } + if ($userBackend !== null) { + $changePasswordForm = new ChangePasswordForm(); + $changePasswordForm + ->setBackend($userBackend) + ->handleRequest(); + $this->view->changePasswordForm = $changePasswordForm; + } + } + + $form = new PreferenceForm(); + $form->setPreferences($user->getPreferences()); + if ($config->get('config_backend', 'ini') !== 'none') { + $form->setStore(PreferencesStore::create(new ConfigObject(array( + 'store' => $config->get('config_backend', 'ini'), + 'resource' => $config->config_resource + )), $user)); + } + $form->handleRequest(); + + $this->view->form = $form; + $this->getTabs()->activate('account'); + } +} diff --git a/application/controllers/AnnouncementsController.php b/application/controllers/AnnouncementsController.php new file mode 100644 index 000000000..458b9a40a --- /dev/null +++ b/application/controllers/AnnouncementsController.php @@ -0,0 +1,100 @@ +getTabs()->add( + 'announcements', + array( + 'active' => true, + 'label' => $this->translate('Announcements'), + 'title' => $this->translate('List All Announcements'), + 'url' => Url::fromPath('announcements') + ) + ); + + $repo = new AnnouncementIniRepository(); + $this->view->announcements = $repo + ->select(array('id', 'author', 'message', 'start', 'end')) + ->order('start'); + } + + /** + * Create an announcement + */ + public function newAction() + { + $this->assertPermission('admin'); + + $form = $this->prepareForm()->add(); + $form->handleRequest(); + $this->renderForm($form, $this->translate('New Announcement')); + } + + /** + * Update an announcement + */ + public function updateAction() + { + $this->assertPermission('admin'); + + $form = $this->prepareForm()->edit($this->params->getRequired('id')); + try { + $form->handleRequest(); + } catch (NotFoundError $_) { + $this->httpNotFound($this->translate('Announcement not found')); + } + $this->renderForm($form, $this->translate('Update Announcement')); + } + + /** + * Remove an announcement + */ + public function removeAction() + { + $this->assertPermission('admin'); + + $form = $this->prepareForm()->remove($this->params->getRequired('id')); + try { + $form->handleRequest(); + } catch (NotFoundError $_) { + $this->httpNotFound($this->translate('Announcement not found')); + } + $this->renderForm($form, $this->translate('Remove Announcement')); + } + + public function acknowledgeAction() + { + $this->assertHttpMethod('POST'); + $this->getResponse()->setHeader('X-Icinga-Container', 'ignore', true); + $form = new AcknowledgeAnnouncementForm(); + $form->handleRequest(); + } + + /** + * Assert permission admin and return a prepared RepositoryForm + * + * @return AnnouncementForm + */ + protected function prepareForm() + { + $form = new AnnouncementForm(); + return $form + ->setRepository(new AnnouncementIniRepository()) + ->setRedirectUrl(Url::fromPath('announcements')); + } +} diff --git a/application/controllers/ApplicationStateController.php b/application/controllers/ApplicationStateController.php index caae26f54..cba019843 100644 --- a/application/controllers/ApplicationStateController.php +++ b/application/controllers/ApplicationStateController.php @@ -4,6 +4,8 @@ namespace Icinga\Controllers; use Icinga\Application\Icinga; +use Icinga\Web\Announcement\AnnouncementCookie; +use Icinga\Web\Announcement\AnnouncementIniRepository; use Icinga\Web\Controller; use Icinga\Web\Session; @@ -14,6 +16,7 @@ class ApplicationStateController extends Controller { public function indexAction() { + $this->_helper->layout()->disableLayout(); if (isset($_COOKIE['icingaweb2-session'])) { $last = (int) $_COOKIE['icingaweb2-session']; } else { @@ -34,6 +37,23 @@ class ApplicationStateController extends Controller ); $_COOKIE['icingaweb2-session'] = $now; } - Icinga::app()->getResponse()->setHeader('X-Icinga-Container', 'ignore', true); + $announcementCookie = new AnnouncementCookie(); + $announcementRepo = new AnnouncementIniRepository(); + if ($announcementCookie->getEtag() !== $announcementRepo->getEtag()) { + $announcementCookie + ->setEtag($announcementRepo->getEtag()) + ->setNextActive($announcementRepo->findNextActive()); + $this->getResponse()->setCookie($announcementCookie); + $this->getResponse()->setHeader('X-Icinga-Announcements', 'refresh', true); + } else { + $nextActive = $announcementCookie->getNextActive(); + if ($nextActive && $nextActive <= $now) { + $announcementCookie->setNextActive($announcementRepo->findNextActive()); + $this->getResponse()->setCookie($announcementCookie); + $this->getResponse()->setHeader('X-Icinga-Announcements', 'refresh', true); + } + } + + $this->getResponse()->setHeader('X-Icinga-Container', 'ignore', true); } } diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index c1d75b711..997c6a708 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -4,6 +4,7 @@ namespace Icinga\Controllers; use Exception; +use Icinga\Application\Version; use InvalidArgumentException; use Icinga\Application\Config; use Icinga\Application\Icinga; @@ -122,6 +123,7 @@ class ConfigController extends Controller $this->view->module = $module; $this->view->tabs = $module->getConfigTabs()->activate('info'); + $this->view->moduleGitCommitId = Version::getGitHead($module->getBaseDir()); } else { $this->view->module = false; $this->view->tabs = null; @@ -213,7 +215,7 @@ class ConfigController extends Controller $form->setOnSuccess(function (UserBackendConfigForm $form) { try { - $form->add(array_filter($form->getValues())); + $form->add($form::transformEmptyValuesToNull($form->getValues())); } catch (Exception $e) { $form->error($e->getMessage()); return false; @@ -244,12 +246,7 @@ class ConfigController extends Controller $form->setIniConfig(Config::app('authentication')); $form->setOnSuccess(function (UserBackendConfigForm $form) use ($backendName) { try { - $form->edit($backendName, array_map( - function ($v) { - return $v !== '' ? $v : null; - }, - $form->getValues() - )); + $form->edit($backendName, $form::transformEmptyValuesToNull($form->getValues())); } catch (Exception $e) { $form->error($e->getMessage()); return false; @@ -393,10 +390,10 @@ class ConfigController extends Controller $authConfig = Config::app('authentication'); foreach ($authConfig as $backendName => $config) { if ($config->get('resource') === $resource) { - $form->addDescription(sprintf( + $form->warning(sprintf( $this->translate( - 'The resource "%s" is currently utilized for authentication by user backend "%s". ' . - 'Removing the resource can result in noone being able to log in any longer.' + 'The resource "%s" is currently utilized for authentication by user backend "%s".' + . ' Removing the resource can result in noone being able to log in any longer.' ), $resource, $backendName @@ -404,6 +401,17 @@ class ConfigController extends Controller } } + // Check if selected resource is currently used as user preferences backend + if (Config::app()->get('global', 'config_resource') === $resource) { + $form->warning(sprintf( + $this->translate( + 'The resource "%s" is currently utilized to store user preferences. Removing the' + . ' resource causes all current user preferences not being available any longer.' + ), + $resource + )); + } + $this->view->form = $form; $this->render('resource/remove'); } diff --git a/application/controllers/ErrorController.php b/application/controllers/ErrorController.php index 86ee26554..b0010385d 100644 --- a/application/controllers/ErrorController.php +++ b/application/controllers/ErrorController.php @@ -32,8 +32,6 @@ class ErrorController extends ActionController $error = $this->_getParam('error_handler'); $exception = $error->exception; /** @var \Exception $exception */ - Logger::error($exception); - Logger::error('Stacktrace: %s', $exception->getTraceAsString()); if (! ($isAuthenticated = $this->Auth()->isAuthenticated())) { $this->innerLayout = 'guest-error'; @@ -83,6 +81,7 @@ class ErrorController extends ActionController break; default: $this->getResponse()->setHttpResponseCode(500); + Logger::error("%s\n%s", $exception, $exception->getTraceAsString()); break; } $this->view->message = $exception->getMessage(); diff --git a/application/controllers/LayoutController.php b/application/controllers/LayoutController.php index f7bbd5102..32295a688 100644 --- a/application/controllers/LayoutController.php +++ b/application/controllers/LayoutController.php @@ -20,4 +20,9 @@ class LayoutController extends ActionController $this->_helper->layout()->disableLayout(); $this->view->menuRenderer = Icinga::app()->getMenu()->getRenderer(); } + + public function announcementsAction() + { + $this->_helper->layout()->disableLayout(); + } } diff --git a/application/controllers/NavigationController.php b/application/controllers/NavigationController.php index 51b5dae1c..1d2d39f68 100644 --- a/application/controllers/NavigationController.php +++ b/application/controllers/NavigationController.php @@ -128,11 +128,11 @@ class NavigationController extends Controller $this->getTabs() ->add( - 'preferences', + 'account', array( - 'title' => $this->translate('Adjust the preferences of Icinga Web 2 according to your needs'), - 'label' => $this->translate('Preferences'), - 'url' => 'preference' + 'title' => $this->translate('Update your account'), + 'label' => $this->translate('My Account'), + 'url' => 'account' ) ) ->add( @@ -219,7 +219,7 @@ class NavigationController extends Controller $form->setDefaultUrl(rawurldecode($this->params->get('url', ''))); $form->setOnSuccess(function (NavigationConfigForm $form) { - $data = array_filter($form->getValues()); + $data = $form::transformEmptyValuesToNull($form->getValues()); try { $form->add($data); @@ -266,12 +266,7 @@ class NavigationController extends Controller $form->setUserConfig(Config::navigation($itemType, $itemOwner)); $form->setRedirectUrl($referrer === 'shared' ? 'navigation/shared' : 'navigation'); $form->setOnSuccess(function (NavigationConfigForm $form) use ($itemName) { - $data = array_map( - function ($v) { - return $v !== '' ? $v : null; - }, - $form->getValues() - ); + $data = $form::transformEmptyValuesToNull($form->getValues()); try { $form->edit($itemName, $data); diff --git a/application/controllers/PreferenceController.php b/application/controllers/PreferenceController.php deleted file mode 100644 index bb4655c27..000000000 --- a/application/controllers/PreferenceController.php +++ /dev/null @@ -1,65 +0,0 @@ - new Tab(array( - 'title' => t('Adjust the preferences of Icinga Web 2 according to your needs'), - 'label' => t('Preferences'), - 'url' => 'preference' - )), - 'navigation' => new Tab(array( - 'title' => t('List and configure your own navigation items'), - 'label' => t('Navigation'), - 'url' => 'navigation' - )) - ); - } - - /** - * Show form to adjust user preferences - */ - public function indexAction() - { - $config = Config::app()->getSection('global'); - $user = $this->getRequest()->getUser(); - - $form = new PreferenceForm(); - $form->setPreferences($user->getPreferences()); - if ($config->get('config_backend', 'ini') !== 'none') { - $form->setStore(PreferencesStore::create(new ConfigObject(array( - 'store' => $config->get('config_backend', 'ini'), - 'resource' => $config->config_resource - )), $user)); - } - $form->handleRequest(); - - $this->view->form = $form; - $this->getTabs()->activate('preferences'); - } -} diff --git a/application/controllers/UsergroupbackendController.php b/application/controllers/UsergroupbackendController.php index 14456fc01..a9e411c54 100644 --- a/application/controllers/UsergroupbackendController.php +++ b/application/controllers/UsergroupbackendController.php @@ -43,7 +43,7 @@ class UsergroupbackendController extends Controller $form->setIniConfig(Config::app('groups')); $form->setOnSuccess(function (UserGroupBackendForm $form) { try { - $form->add(array_filter($form->getValues())); + $form->add($form::transformEmptyValuesToNull($form->getValues())); } catch (Exception $e) { $form->error($e->getMessage()); return false; @@ -73,12 +73,7 @@ class UsergroupbackendController extends Controller $form->setIniConfig(Config::app('groups')); $form->setOnSuccess(function (UserGroupBackendForm $form) use ($backendName) { try { - $form->edit($backendName, array_map( - function ($v) { - return $v !== '' ? $v : null; - }, - $form->getValues() - )); + $form->edit($backendName, $form::transformEmptyValuesToNull($form->getValues())); } catch (Exception $e) { $form->error($e->getMessage()); return false; diff --git a/application/fonts/fontello-ifont/LICENSE.txt b/application/fonts/fontello-ifont/LICENSE.txt old mode 100644 new mode 100755 index 7e9af2aa8..441bc985b --- a/application/fonts/fontello-ifont/LICENSE.txt +++ b/application/fonts/fontello-ifont/LICENSE.txt @@ -3,7 +3,7 @@ Font license info ## Font Awesome - Copyright (C) 2012 by Dave Gandy + Copyright (C) 2016 by Dave Gandy Author: Dave Gandy License: SIL () @@ -19,15 +19,6 @@ Font license info Homepage: http://somerandomdude.com/work/iconic/ -## MFG Labs - - Copyright (C) 2012 by Daniel Bruce - - Author: MFG Labs - License: SIL (http://scripts.sil.org/OFL) - Homepage: http://www.mfglabs.com/ - - ## Entypo Copyright (C) 2012 by Daniel Bruce diff --git a/application/fonts/fontello-ifont/README.txt b/application/fonts/fontello-ifont/README.txt old mode 100644 new mode 100755 index a91438a9a..beaab3366 --- a/application/fonts/fontello-ifont/README.txt +++ b/application/fonts/fontello-ifont/README.txt @@ -2,14 +2,14 @@ This webfont is generated by http://fontello.com open source project. ================================================================================ -Please, note, that you should obey original font licences, used to make this +Please, note, that you should obey original font licenses, used to make this webfont pack. Details available in LICENSE.txt file. - Usually, it's enough to publish content of LICENSE.txt file somewhere on your site in "About" section. - If your project is open-source, usually, it will be ok to make LICENSE.txt - file publically available in your repository. + file publicly available in your repository. - Fonts, used in Fontello, don't require a clickable link on your site. But any kind of additional authors crediting is welcome. diff --git a/application/fonts/fontello-ifont/config.json b/application/fonts/fontello-ifont/config.json old mode 100644 new mode 100755 index f68fd5cef..610be9441 --- a/application/fonts/fontello-ifont/config.json +++ b/application/fonts/fontello-ifont/config.json @@ -711,7 +711,7 @@ { "uid": "3bd18d47a12b8709e9f4fe9ead4f7518", "css": "reschedule", - "code": 59492, + "code": 59524, "src": "entypo" }, { @@ -744,12 +744,6 @@ "code": 59512, "src": "typicons" }, - { - "uid": "b90d80c250a9bbdd6cd3fe00e6351710", - "css": "ok", - "code": 59395, - "src": "iconic" - }, { "uid": "11e664deed5b2587456a4f9c01d720b6", "css": "cancel", @@ -775,10 +769,52 @@ "src": "iconic" }, { - "uid": "9c3b8d8a6d477da4d3e65b92e4e9c290", + "uid": "8f28d948aa6379b1a69d2a090e7531d4", + "css": "warning-empty", + "code": 59525, + "src": "typicons" + }, + { + "uid": "d4816c0845aa43767213d45574b3b145", + "css": "history", + "code": 61914, + "src": "fontawesome" + }, + { + "uid": "b035c28eba2b35c6ffe92aee8b0df507", + "css": "attention-circled", + "code": 59521, + "src": "fontawesome" + }, + { + "uid": "73ffeb70554099177620847206c12457", + "css": "binoculars", + "code": 61925, + "src": "fontawesome" + }, + { + "uid": "a73c5deb486c8d66249811642e5d719a", + "css": "arrows-cw", + "code": 59492, + "src": "fontawesome" + }, + { + "uid": "dd6c6b221a1088ff8a9b9cd32d0b3dd5", + "css": "check", + "code": 59523, + "src": "fontawesome" + }, + { + "uid": "b90d80c250a9bbdd6cd3fe00e6351710", + "css": "ok", + "code": 59395, + "src": "iconic" + }, + { + "uid": "37c5ab63f10d7ad0b84d0978dcd0c7a8", "css": "flapping", "code": 59485, - "src": "mfglabs" + "src": "fontawesome" } ] } \ No newline at end of file diff --git a/application/fonts/fontello-ifont/css/animation.css b/application/fonts/fontello-ifont/css/animation.css old mode 100644 new mode 100755 diff --git a/application/fonts/fontello-ifont/css/ifont-codes.css b/application/fonts/fontello-ifont/css/ifont-codes.css old mode 100644 new mode 100755 index 146f097f0..3c9341de5 --- a/application/fonts/fontello-ifont/css/ifont-codes.css +++ b/application/fonts/fontello-ifont/css/ifont-codes.css @@ -99,7 +99,7 @@ .icon-bell-off-empty:before { content: '\e861'; } /* '' */ .icon-plug:before { content: '\e862'; } /* '' */ .icon-eye-off:before { content: '\e863'; } /* '' */ -.icon-reschedule:before { content: '\e864'; } /* '' */ +.icon-arrows-cw:before { content: '\e864'; } /* '' */ .icon-cw:before { content: '\e865'; } /* '' */ .icon-host:before { content: '\e866'; } /* '' */ .icon-thumbs-up:before { content: '\e867'; } /* '' */ @@ -127,4 +127,10 @@ .icon-info-circled:before { content: '\e87d'; } /* '' */ .icon-twitter:before { content: '\e87e'; } /* '' */ .icon-facebook-squared:before { content: '\e87f'; } /* '' */ -.icon-gplus-squared:before { content: '\e880'; } /* '' */ \ No newline at end of file +.icon-gplus-squared:before { content: '\e880'; } /* '' */ +.icon-attention-circled:before { content: '\e881'; } /* '' */ +.icon-check:before { content: '\e883'; } /* '' */ +.icon-reschedule:before { content: '\e884'; } /* '' */ +.icon-warning-empty:before { content: '\e885'; } /* '' */ +.icon-history:before { content: '\f1da'; } /* '' */ +.icon-binoculars:before { content: '\f1e5'; } /* '' */ \ No newline at end of file diff --git a/application/fonts/fontello-ifont/css/ifont-embedded.css b/application/fonts/fontello-ifont/css/ifont-embedded.css old mode 100644 new mode 100755 index d8330a657..f9fa8b3ea --- a/application/fonts/fontello-ifont/css/ifont-embedded.css +++ b/application/fonts/fontello-ifont/css/ifont-embedded.css @@ -1,15 +1,15 @@ @font-face { font-family: 'ifont'; - src: url('../font/ifont.eot?36375038'); - src: url('../font/ifont.eot?36375038#iefix') format('embedded-opentype'), - url('../font/ifont.svg?36375038#ifont') format('svg'); + src: url('../font/ifont.eot?47279460'); + src: url('../font/ifont.eot?47279460#iefix') format('embedded-opentype'), + url('../font/ifont.svg?47279460#ifont') format('svg'); font-weight: normal; font-style: normal; } @font-face { font-family: 'ifont'; - src: url('data:application/octet-stream;base64,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') format('woff'), - url('data:application/octet-stream;base64,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BjYGFQ4BFS4BNx4BFxYHBgcGFxQGFgGtdMZycsboyG4GerwBEgEIAwECBAMRFRMKAQwEDAMBBwYEBAoFBgQBCAEGAQQEBAIEBgEGAggJBQQFBQMBCAwBBRwHAgIBCAEOAQIHCQMEBAEEAgMBBwoCBAUNBAIUDhMECAYBAgECBQkCARMJAgQGBQYKAwgEBwUDAgYJBAYBBQkEBQMDAgUEAQ4HCw8EEAMDAQgECAEIAwEIBAQEAwMEAgQSBQMMDAEDAwIMGRsDAwgFEwUDCwQNCwEEAgYECAQJBFEyBAUCBgUDARgKAQIHBQQDBAQEAQIBAQECCgcHEgQHCQQDCAQCDgEBAgIOAgQCAg8IAwQDAgMFAQQKCgEECAQFDAcCAwgDCQcWBgYFCAgQBBQKAQIEAgYDDgMEAQoFCBEKAgICAgEFAgQBCgIDDAMCCAECCAMBAwIHCwQBAgIIFAMICgECAQQCAwUCAQIBBAECAgQYAwkDAQEBAw0CDgQCAwEEAwUCBggEAgIBCAQEBwgFBwwEBAICAgYBBQQDAgMFBwQDAhIBBAICBQwCCQICCggFCQIIBAIKCQ0JaXJRAQwBDQEEAxUBAwUCAwICAQUMCAMEBQEKAQMBAQQIBAoBBwYCCgIEAQwBAQICBAsPAQIJCgEDC3TE6sR0dMTqxHT+3QEIAgYGAQQIAwULAQwCAgQMAQoHAgMEAgQBAgYMBQYDCgEGBAEBAgICAQMDAgEDCAQCBgIDAwQFBAYHBAYICgcEBQYFDAMBAgQCAQMMCQ4DBAUHCAUDEQIDDgcGDAMBAwkCBwoDBgEOBAoEAQIFAgIGCgQHBwcBCQUIBwgDAgcDAgQCBgIEBQoDAw4CBQEBAgUEBwIBCggPAQMCAgcEAw4DAgQDBwMGBAQBAS1PBAEIBAMEBg8KAgYEBQQFDgkUCwIBBhoCARcFBAYDBRQDAxAFAgEECAUIBAELFw4FDAICBAQMCA4EDgEKCxQHCAEFAw0CAQIBEgMKBAQJBQYCAwoDAgMFDAIQCRMDAwQEBgIECgcOAQUCBAEEAgIQBQ8FAgUDAgsCCAQEAgIEGA4JDgUJAQQGAQIDAQEBBAMGBwYFAg8KAQQBAgMBAgMIBRcEAggIAwQPAgoKBQECAwQLCQUCAgICBgIKBwYFBAQEAwEECgQGAQcCAQcGBQMEAQEBBQQC/g0VVQIC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IABiAHkAIABzAHYAZwAyAHQAdABmACAAZgByAG8AbQAgAEYAbwBuAHQAZQBsAGwAbwAgAHAAcgBvAGoAZQBjAHQALgBoAHQAdABwADoALwAvAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAAAAAAIAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAggAAAQIBAwEEAQUBBgEHAQgBCQEKAQsBDAENAQ4BDwEQAREBEgETARQBFQEWARcBGAEZARoBGwEcAR0BHgEfASABIQEiASMBJAElASYBJwEoASkBKgErASwBLQEuAS8BMAExATIBMwE0ATUBNgE3ATgBOQE6ATsBPAE9AT4BPwFAAUEBQgFDAUQBRQFGAUcBSAFJAUoBSwFMAU0BTgFPAVABUQFSAVMBVAFVAVYBVwFYAVkBWgFbAVwBXQFeAV8BYAFhAWIBYwFkAWUBZgFnAWgBaQFqAWsBbAFtAW4BbwFwAXEBcgFzAXQBdQF2AXcBeAF5AXoBewF8AX0BfgF/AYABgQGCCWRhc2hib2FyZAR1c2VyBXVzZXJzAm9rBmNhbmNlbARwbHVzBW1pbnVzDGZvbGRlci1lbXB0eQhkb3dubG9hZAZ1cGxvYWQDZ2l0BWN1YmVzCGRhdGFiYXNlBWdhdWdlB3NpdGVtYXAMc29ydC1uYW1lLXVwDnNvcnQtbmFtZS1kb3duCW1lZ2FwaG9uZQNidWcFdGFza3MGZmlsdGVyA29mZgRib29rBXBhc3RlCHNjaXNzb3JzBWdsb2JlBWNsb3VkBWZsYXNoCGJhcmNoYXJ0CGRvd24tZGlyBnVwLWRpcghsZWZ0LWRpcglyaWdodC1kaXIJZG93bi1vcGVuCnJpZ2h0LW9wZW4HdXAtb3BlbglsZWZ0LW9wZW4GdXAtYmlnCXJpZ2h0LWJpZwhsZWZ0LWJpZwhkb3duLWJpZw9yZXNpemUtZnVsbC1hbHQLcmVzaXplLWZ1bGwMcmVzaXplLXNtYWxsBG1vdmURcmVzaXplLWhvcml6b250YWwPcmVzaXplLXZlcnRpY2FsB3pvb20taW4FYmxvY2sIem9vbS1vdXQJbGlnaHRidWxiBWNsb2NrCXZvbHVtZS11cAt2b2x1bWUtZG93bgp2b2x1bWUtb2ZmBG11dGUDbWljB2VuZHRpbWUJc3RhcnR0aW1lDmNhbGVuZGFyLWVtcHR5CGNhbGVuZGFyBndyZW5jaAdzbGlkZXJzCHNlcnZpY2VzB3NlcnZpY2UFcGhvbmUIZmlsZS1wZGYJZmlsZS13b3JkCmZpbGUtZXhjZWwIZG9jLXRleHQFdHJhc2gNY29tbWVudC1lbXB0eQdjb21tZW50BGNoYXQKY2hhdC1lbXB0eQRiZWxsCGJlbGwtYWx0DWF0dGVudGlvbi1hbHQFcHJpbnQEZWRpdAdmb3J3YXJkBXJlcGx5CXJlcGx5LWFsbANleWUDdGFnBHRhZ3MNbG9jay1vcGVuLWFsdAlsb2NrLW9wZW4EbG9jawRob21lBGluZm8EaGVscAZzZWFyY2gIZmxhcHBpbmcGcmV3aW5kCmNoYXJ0LWxpbmUIYmVsbC1vZmYOYmVsbC1vZmYtZW1wdHkEcGx1ZwdleWUtb2ZmCnJlc2NoZWR1bGUCY3cEaG9zdAl0aHVtYnMtdXALdGh1bWJzLWRvd24Hc3Bpbm5lcgZhdHRhY2gIa2V5Ym9hcmQEbWVudQR3aWZpBG1vb24JY2hhcnQtcGllCmNoYXJ0LWFyZWEJY2hhcnQtYmFyBmJlYWtlcgVtYWdpYwVzcGluNgpkb3duLXNtYWxsCmxlZnQtc21hbGwLcmlnaHQtc21hbGwIdXAtc21hbGwDcGluEWFuZ2xlLWRvdWJsZS1sZWZ0EmFuZ2xlLWRvdWJsZS1yaWdodAZjaXJjbGUMaW5mby1jaXJjbGVkB3R3aXR0ZXIQZmFjZWJvb2stc3F1YXJlZA1ncGx1cy1zcXVhcmVkAAAAAAEAAf//AA8AAAAAAAAAAAAAAACwACwgsABVWEVZICBLuAAOUUuwBlNaWLA0G7AoWWBmIIpVWLACJWG5CAAIAGNjI2IbISGwAFmwAEMjRLIAAQBDYEItsAEssCBgZi2wAiwgZCCwwFCwBCZasigBCkNFY0VSW1ghIyEbilggsFBQWCGwQFkbILA4UFghsDhZWSCxAQpDRWNFYWSwKFBYIbEBCkNFY0UgsDBQWCGwMFkbILDAUFggZiCKimEgsApQWGAbILAgUFghsApgGyCwNlBYIbA2YBtgWVlZG7ABK1lZI7AAUFhlWVktsAMsIEUgsAQlYWQgsAVDUFiwBSNCsAYjQhshIVmwAWAtsAQsIyEjISBksQViQiCwBiNCsQEK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') format('truetype'); + src: url('data:application/octet-stream;base64,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') format('woff'), + url('data:application/octet-stream;base64,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AAAAPQAA6D0AAOg9AAAAPgAA6D4AAOg+AAAAPwAA6D8AAOg/AAAAQAAA6EAAAOhAAAAAQQAA6EEAAOhBAAAAQgAA6EIAAOhCAAAAQwAA6EMAAOhDAAAARAAA6EQAAOhEAAAARQAA6EUAAOhFAAAARgAA6EYAAOhGAAAARwAA6EcAAOhHAAAASAAA6EgAAOhIAAAASQAA6EkAAOhJAAAASgAA6EoAAOhKAAAASwAA6EsAAOhLAAAATAAA6EwAAOhMAAAATQAA6E0AAOhNAAAATgAA6E4AAOhOAAAATwAA6E8AAOhPAAAAUAAA6FAAAOhQAAAAUQAA6FEAAOhRAAAAUgAA6FIAAOhSAAAAUwAA6FMAAOhTAAAAVAAA6FQAAOhUAAAAVQAA6FUAAOhVAAAAVgAA6FYAAOhWAAAAVwAA6FcAAOhXAAAAWAAA6FgAAOhYAAAAWQAA6FkAAOhZAAAAWgAA6FoAAOhaAAAAWwAA6FsAAOhbAAAAXAAA6FwAAOhcAAAAXQAA6F0AAOhdAAAAXgAA6F4AAOheAAAAXwAA6F8AAOhfAAAAYAAA6GAAAOhgAAAAYQAA6GEAAOhhAAAAYgAA6GIAAOhiAAAAYwAA6GMAAOhjAAAAZAAA6GQAAOhkAAAAZQAA6GUAAOhlAAAAZgAA6GYAAOhmAAAAZwAA6GcAAOhnAAAAaAAA6GgAAOhoAAAAaQAA6GkAAOhpAAAAagAA6GoAAOhqAAAAawAA6GsAAOhrAAAAbAAA6GwAAOhsAAAAbQAA6G0AAOhtAAAAbgAA6G4AAOhuAAAAbwAA6G8AAOhvAAAAcAAA6HAAAOhwAAAAcQAA6HEAAOhxAAAAcgAA6HIAAOhyAAAAcwAA6HMAAOhzAAAAdAAA6HQAAOh0AAAAdQAA6HUAAOh1AAAAdgAA6HYAAOh2AAAAdwAA6HcAAOh3AAAAeAAA6HgAAOh4AAAAeQAA6HkAAOh5AAAAegAA6HoAAOh6AAAAewAA6HsAAOh7AAAAfAAA6HwAAOh8AAAAfQAA6H0AAOh9AAAAfgAA6H4AAOh+AAAAfwAA6H8AAOh/AAAAgAAA6IAAAOiAAAAAgQAA6IEAAOiBAAAAggAA6IMAAOiDAAAAgwAA6IQAAOiEAAAAhAAA6IUAAOiFAAAAhQAA8doAAPHaAAAAhgAA8eUAAPHlAAAAhwAJAAD/+QPo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AREBEgETARQBFQEWARcBGAEZARoBGwEcAR0BHgEfASABIQEiASMBJAElASYBJwEoASkBKgErASwBLQEuAS8BMAExATIBMwE0ATUBNgE3ATgBOQE6ATsBPAE9AT4BPwFAAUEBQgFDAUQBRQFGAUcBSAFJAUoBSwFMAU0BTgFPAVABUQFSAVMBVAFVAVYBVwFYAVkBWgFbAVwBXQFeAV8BYAFhAWIBYwFkAWUBZgFnAWgBaQFqAWsBbAFtAW4BbwFwAXEBcgFzAXQBdQF2AXcBeAF5AXoBewF8AX0BfgF/AYABgQGCAYMBhAGFAYYBhwGIAYkACWRhc2hib2FyZAR1c2VyBXVzZXJzAm9rBmNhbmNlbARwbHVzBW1pbnVzDGZvbGRlci1lbXB0eQhkb3dubG9hZAZ1cGxvYWQDZ2l0BWN1YmVzCGRhdGFiYXNlBWdhdWdlB3NpdGVtYXAMc29ydC1uYW1lLXVwDnNvcnQtbmFtZS1kb3duCW1lZ2FwaG9uZQNidWcFdGFza3MGZmlsdGVyA29mZgRib29rBXBhc3RlCHNjaXNzb3JzBWdsb2JlBWNsb3VkBWZsYXNoCGJhcmNoYXJ0CGRvd24tZGlyBnVwLWRpcghsZWZ0LWRpcglyaWdodC1kaXIJZG93bi1vcGVuCnJpZ2h0LW9wZW4HdXAtb3BlbglsZWZ0LW9wZW4GdXAtYmlnCXJpZ2h0LWJpZwhsZWZ0LWJpZwhkb3duLWJpZw9yZXNpemUtZnVsbC1hbHQLcmVzaXplLWZ1bGwMcmVzaXplLXNtYWxsBG1vdmURcmVzaXplLWhvcml6b250YWwPcmVzaXplLXZlcnRpY2FsB3pvb20taW4FYmxvY2sIem9vbS1vdXQJbGlnaHRidWxiBWNsb2NrCXZvbHVtZS11cAt2b2x1bWUtZG93bgp2b2x1bWUtb2ZmBG11dGUDbWljB2VuZHRpbWUJc3RhcnR0aW1lDmNhbGVuZGFyLWVtcHR5CGNhbGVuZGFyBndyZW5jaAdzbGlkZXJzCHNlcnZpY2VzB3NlcnZpY2UFcGhvbmUIZmlsZS1wZGYJZmlsZS13b3JkCmZpbGUtZXhjZWwIZG9jLXRleHQFdHJhc2gNY29tbWVudC1lbXB0eQdjb21tZW50BGNoYXQKY2hhdC1lbXB0eQRiZWxsCGJlbGwtYWx0DWF0dGVudGlvbi1hbHQFcHJpbnQEZWRpdAdmb3J3YXJkBXJlcGx5CXJlcGx5LWFsbANleWUDdGFnBHRhZ3MNbG9jay1vcGVuLWFsdAlsb2NrLW9wZW4EbG9jawRob21lBGluZm8EaGVscAZzZWFyY2gIZmxhcHBpbmcGcmV3aW5kCmNoYXJ0LWxpbmUIYmVsbC1vZmYOYmVsbC1vZmYtZW1wdHkEcGx1ZwdleWUtb2ZmCWFycm93cy1jdwJjdwRob3N0CXRodW1icy11cAt0aHVtYnMtZG93bgdzcGlubmVyBmF0dGFjaAhrZXlib2FyZARtZW51BHdpZmkEbW9vbgljaGFydC1waWUKY2hhcnQtYXJlYQljaGFydC1iYXIGYmVha2VyBW1hZ2ljBXNwaW42CmRvd24tc21hbGwKbGVmdC1zbWFsbAtyaWdodC1zbWFsbAh1cC1zbWFsbANwaW4RYW5nbGUtZG91YmxlLWxlZnQSYW5nbGUtZG91YmxlLXJpZ2h0BmNpcmNsZQxpbmZvLWNpcmNsZWQHdHdpdHRlchBmYWNlYm9vay1zcXVhcmVkDWdwbHVzLXNxdWFyZWQRYXR0ZW50aW9uLWNpcmNsZWQFY2hlY2sKcmVzY2hlZHVsZQ13YXJuaW5nLWVtcHR5B2hpc3RvcnkKYmlub2N1bGFycwAAAAABAAH//wAPAAAAAAAAAAAAAAAAAAAAAAAYABgAGAAYA1P/aQNT/2mwACwgsABVWEVZICBLuAAOUUuwBlNaWLA0G7AoWWBmIIpVWLACJWG5CAAIAGNjI2IbISGwAFmwAEMjRLIAAQBDYEItsAEssCBgZi2wAiwgZCCwwFCwBCZasigBCkNFY0VSW1ghIyEbilggsFBQWCGwQFkbILA4UFghsDhZWSCxAQpDRWNFYWSwKFBYIbEBCkNFY0UgsDBQWCGwMFkbILDAUFggZiCKimEgsApQWGAbILAgUFghsApgGyCwNlBYIbA2YBtgWVlZG7ABK1lZI7AAUFhlWVktsAMsIEUgsAQlYWQgsAVDUFiwBSNCsAYjQhshIVmwAWAtsAQsIyEjISBksQViQiCwBiNCsQEKQ0VjsQEKQ7ABYEVjsAMqISCwBkMgiiCKsAErsTAFJbAEJlFYYFAbYVJZWCNZISCwQFNYsAErGyGwQFkjsABQWGVZLbAFLLAH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') format('truetype'); } /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ @@ -17,7 +17,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'ifont'; - src: url('../font/ifont.svg?36375038#ifont') format('svg'); + src: url('../font/ifont.svg?47279460#ifont') format('svg'); } } */ @@ -152,7 +152,7 @@ .icon-bell-off-empty:before { content: '\e861'; } /* '' */ .icon-plug:before { content: '\e862'; } /* '' */ .icon-eye-off:before { content: '\e863'; } /* '' */ -.icon-reschedule:before { content: '\e864'; } /* '' */ +.icon-arrows-cw:before { content: '\e864'; } /* '' */ .icon-cw:before { content: '\e865'; } /* '' */ .icon-host:before { content: '\e866'; } /* '' */ .icon-thumbs-up:before { content: '\e867'; } /* '' */ @@ -180,4 +180,10 @@ .icon-info-circled:before { content: '\e87d'; } /* '' */ .icon-twitter:before { content: '\e87e'; } /* '' */ .icon-facebook-squared:before { content: '\e87f'; } /* '' */ -.icon-gplus-squared:before { content: '\e880'; } /* '' */ \ No newline at end of file +.icon-gplus-squared:before { content: '\e880'; } /* '' */ +.icon-attention-circled:before { content: '\e881'; } /* '' */ +.icon-check:before { content: '\e883'; } /* '' */ +.icon-reschedule:before { content: '\e884'; } /* '' */ +.icon-warning-empty:before { content: '\e885'; } /* '' */ +.icon-history:before { content: '\f1da'; } /* '' */ +.icon-binoculars:before { content: '\f1e5'; } /* '' */ \ No newline at end of file diff --git a/application/fonts/fontello-ifont/css/ifont-ie7-codes.css b/application/fonts/fontello-ifont/css/ifont-ie7-codes.css old mode 100644 new mode 100755 index 660e08a3d..c50f57570 --- a/application/fonts/fontello-ifont/css/ifont-ie7-codes.css +++ b/application/fonts/fontello-ifont/css/ifont-ie7-codes.css @@ -99,7 +99,7 @@ .icon-bell-off-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-plug { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-eye-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-reschedule { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-arrows-cw { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-cw { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-host { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-thumbs-up { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } @@ -127,4 +127,10 @@ .icon-info-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-facebook-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-gplus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file +.icon-gplus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-attention-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-check { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-reschedule { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-warning-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-history { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-binoculars { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file diff --git a/application/fonts/fontello-ifont/css/ifont-ie7.css b/application/fonts/fontello-ifont/css/ifont-ie7.css old mode 100644 new mode 100755 index a3c15c2d2..8c3401a9d --- a/application/fonts/fontello-ifont/css/ifont-ie7.css +++ b/application/fonts/fontello-ifont/css/ifont-ie7.css @@ -110,7 +110,7 @@ .icon-bell-off-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-plug { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-eye-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-reschedule { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-arrows-cw { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-cw { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-host { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-thumbs-up { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } @@ -138,4 +138,10 @@ .icon-info-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-facebook-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-gplus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file +.icon-gplus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-attention-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-check { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-reschedule { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-warning-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-history { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-binoculars { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file diff --git a/application/fonts/fontello-ifont/css/ifont.css b/application/fonts/fontello-ifont/css/ifont.css old mode 100644 new mode 100755 index 9eba001ae..adbfd04ec --- a/application/fonts/fontello-ifont/css/ifont.css +++ b/application/fonts/fontello-ifont/css/ifont.css @@ -1,10 +1,11 @@ @font-face { font-family: 'ifont'; - src: url('../font/ifont.eot?65389432'); - src: url('../font/ifont.eot?65389432#iefix') format('embedded-opentype'), - url('../font/ifont.woff?65389432') format('woff'), - url('../font/ifont.ttf?65389432') format('truetype'), - url('../font/ifont.svg?65389432#ifont') format('svg'); + src: url('../font/ifont.eot?38679513'); + src: url('../font/ifont.eot?38679513#iefix') format('embedded-opentype'), + url('../font/ifont.woff2?38679513') format('woff2'), + url('../font/ifont.woff?38679513') format('woff'), + url('../font/ifont.ttf?38679513') format('truetype'), + url('../font/ifont.svg?38679513#ifont') format('svg'); font-weight: normal; font-style: normal; } @@ -14,7 +15,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'ifont'; - src: url('../font/ifont.svg?65389432#ifont') format('svg'); + src: url('../font/ifont.svg?38679513#ifont') format('svg'); } } */ @@ -154,7 +155,7 @@ .icon-bell-off-empty:before { content: '\e861'; } /* '' */ .icon-plug:before { content: '\e862'; } /* '' */ .icon-eye-off:before { content: '\e863'; } /* '' */ -.icon-reschedule:before { content: '\e864'; } /* '' */ +.icon-arrows-cw:before { content: '\e864'; } /* '' */ .icon-cw:before { content: '\e865'; } /* '' */ .icon-host:before { content: '\e866'; } /* '' */ .icon-thumbs-up:before { content: '\e867'; } /* '' */ @@ -182,4 +183,10 @@ .icon-info-circled:before { content: '\e87d'; } /* '' */ .icon-twitter:before { content: '\e87e'; } /* '' */ .icon-facebook-squared:before { content: '\e87f'; } /* '' */ -.icon-gplus-squared:before { content: '\e880'; } /* '' */ \ No newline at end of file +.icon-gplus-squared:before { content: '\e880'; } /* '' */ +.icon-attention-circled:before { content: '\e881'; } /* '' */ +.icon-check:before { content: '\e883'; } /* '' */ +.icon-reschedule:before { content: '\e884'; } /* '' */ +.icon-warning-empty:before { content: '\e885'; } /* '' */ +.icon-history:before { content: '\f1da'; } /* '' */ +.icon-binoculars:before { content: '\f1e5'; } /* '' */ \ No newline at end of file diff --git a/application/fonts/fontello-ifont/demo.html b/application/fonts/fontello-ifont/demo.html old mode 100644 new mode 100755 index 2b409f7c8..d60d62e5f --- a/application/fonts/fontello-ifont/demo.html +++ b/application/fonts/fontello-ifont/demo.html @@ -229,11 +229,11 @@ body { } @font-face { font-family: 'ifont'; - src: url('./font/ifont.eot?43849680'); - src: url('./font/ifont.eot?43849680#iefix') format('embedded-opentype'), - url('./font/ifont.woff?43849680') format('woff'), - url('./font/ifont.ttf?43849680') format('truetype'), - url('./font/ifont.svg?43849680#ifont') format('svg'); + src: url('./font/ifont.eot?54126565'); + src: url('./font/ifont.eot?54126565#iefix') format('embedded-opentype'), + url('./font/ifont.woff?54126565') format('woff'), + url('./font/ifont.ttf?54126565') format('truetype'), + url('./font/ifont.svg?54126565#ifont') format('svg'); font-weight: normal; font-style: normal; } @@ -279,7 +279,7 @@ body { +

diff --git a/application/layouts/scripts/pdf.phtml b/application/layouts/scripts/pdf.phtml
index 68c341e01..13e383958 100644
--- a/application/layouts/scripts/pdf.phtml
+++ b/application/layouts/scripts/pdf.phtml
@@ -31,7 +31,7 @@ if ( isset($pdf) )
 }
 
 
-img('img/logo_icinga_big_dark.png', null, array('align' => 'right', 'width' => '75')) ?>
+img('img/icinga-logo-big-dark.png', null, array('align' => 'right', 'width' => '75')) ?>
 ');
+            if ($comment_start === false ||
+                ($comment_end !== false && $comment_end > $comment_start)) {
+                return $matches[2];
+            }
         }
+        return $html;
     }
 }
 
diff --git a/library/vendor/HTMLPurifier/Printer/ConfigForm.php b/library/vendor/HTMLPurifier/Printer/ConfigForm.php
index 36100ce73..65a777904 100644
--- a/library/vendor/HTMLPurifier/Printer/ConfigForm.php
+++ b/library/vendor/HTMLPurifier/Printer/ConfigForm.php
@@ -327,6 +327,10 @@ class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer
                 case HTMLPurifier_VarParser::HASH:
                     $nvalue = '';
                     foreach ($value as $i => $v) {
+                        if (is_array($v)) {
+                            // HACK
+                            $v = implode(";", $v);
+                        }
                         $nvalue .= "$i:$v" . PHP_EOL;
                     }
                     $value = $nvalue;
diff --git a/library/vendor/HTMLPurifier/SOURCE b/library/vendor/HTMLPurifier/SOURCE
index 44e19b159..bdc316957 100644
--- a/library/vendor/HTMLPurifier/SOURCE
+++ b/library/vendor/HTMLPurifier/SOURCE
@@ -1,7 +1,10 @@
-curl https://codeload.github.com/ezyang/htmlpurifier/tar.gz/v4.7.0 -o htmlpurifier-4.7.0.tar.gz
-tar xzf htmlpurifier-4.7.0.tar.gz --strip-components 1 htmlpurifier-4.7.0/LICENSE
-tar xzf htmlpurifier-4.7.0.tar.gz --strip-components 1 htmlpurifier-4.7.0/VERSION
-tar xzf htmlpurifier-4.7.0.tar.gz -C ../ --strip-components 2 htmlpurifier-4.7.0/library/HTMLPurifier.php
-tar xzf htmlpurifier-4.7.0.tar.gz -C ../ --strip-components 2 htmlpurifier-4.7.0/library/HTMLPurifier.autoload.php
-tar xzf htmlpurifier-4.7.0.tar.gz --strip-components 3 htmlpurifier-4.7.0/library/HTMLPurifier/*
-rm htmlpurifier-4.7.0.tar.gz
+GLOBIGNORE=$0; rm -rf *
+rm ../HTMLPurifier*.php
+
+curl https://codeload.github.com/ezyang/htmlpurifier/tar.gz/v4.8.0 -o htmlpurifier-4.8.0.tar.gz
+tar xzf htmlpurifier-4.8.0.tar.gz --strip-components 1 htmlpurifier-4.8.0/LICENSE
+tar xzf htmlpurifier-4.8.0.tar.gz --strip-components 1 htmlpurifier-4.8.0/VERSION
+tar xzf htmlpurifier-4.8.0.tar.gz -C ../ --strip-components 2 htmlpurifier-4.8.0/library/HTMLPurifier.php
+tar xzf htmlpurifier-4.8.0.tar.gz -C ../ --strip-components 2 htmlpurifier-4.8.0/library/HTMLPurifier.autoload.php
+tar xzf htmlpurifier-4.8.0.tar.gz --strip-components 3 htmlpurifier-4.8.0/library/HTMLPurifier/*
+rm htmlpurifier-4.8.0.tar.gz
diff --git a/library/vendor/HTMLPurifier/URIScheme/data.php b/library/vendor/HTMLPurifier/URIScheme/data.php
index 6ebca4984..41c49d553 100644
--- a/library/vendor/HTMLPurifier/URIScheme/data.php
+++ b/library/vendor/HTMLPurifier/URIScheme/data.php
@@ -79,9 +79,18 @@ class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme
         } else {
             $raw_data = $data;
         }
+        if ( strlen($raw_data) < 12 ) {
+            // error; exif_imagetype throws exception with small files,
+            // and this likely indicates a corrupt URI/failed parse anyway
+            return false;
+        }
         // XXX probably want to refactor this into a general mechanism
         // for filtering arbitrary content types
-        $file = tempnam("/tmp", "");
+        if (function_exists('sys_get_temp_dir')) {
+            $file = tempnam(sys_get_temp_dir(), "");
+        } else {
+            $file = tempnam("/tmp", "");
+        }
         file_put_contents($file, $raw_data);
         if (function_exists('exif_imagetype')) {
             $image_code = exif_imagetype($file);
diff --git a/library/vendor/HTMLPurifier/URIScheme/tel.php b/library/vendor/HTMLPurifier/URIScheme/tel.php
new file mode 100644
index 000000000..8cd193352
--- /dev/null
+++ b/library/vendor/HTMLPurifier/URIScheme/tel.php
@@ -0,0 +1,46 @@
+userinfo = null;
+        $uri->host     = null;
+        $uri->port     = null;
+
+        // Delete all non-numeric characters, non-x characters
+        // from phone number, EXCEPT for a leading plus sign.
+        $uri->path = preg_replace('/(?!^\+)[^\dx]/', '',
+                     // Normalize e(x)tension to lower-case
+                     str_replace('X', 'x', $uri->path));
+
+        return true;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/vendor/HTMLPurifier/VERSION b/library/vendor/HTMLPurifier/VERSION
index 1163055e2..6ca6df113 100644
--- a/library/vendor/HTMLPurifier/VERSION
+++ b/library/vendor/HTMLPurifier/VERSION
@@ -1 +1 @@
-4.7.0
\ No newline at end of file
+4.8.0
\ No newline at end of file
diff --git a/library/vendor/dompdf/SOURCE b/library/vendor/dompdf/SOURCE
index babe1b191..3eb54e1fd 100644
--- a/library/vendor/dompdf/SOURCE
+++ b/library/vendor/dompdf/SOURCE
@@ -1,9 +1,16 @@
-curl https://codeload.github.com/dompdf/dompdf/tar.gz/v0.6.2 -o dompdf-0.6.2.tar.gz
-tar xzf dompdf-0.6.2.tar.gz --strip-components 1 --wildcards dompdf-0.6.2/{include/\*.php,lib,dompdf\*.php,LICENSE.LGPL}
-rm dompdf-0.6.2.tar.gz
+GLOBIGNORE=$0; rm -rf *
+
+curl https://codeload.github.com/dompdf/dompdf/tar.gz/v0.7.0 -o dompdf-0.7.0.tar.gz
+tar xzf dompdf-0.7.0.tar.gz --strip-components 1 dompdf-0.7.0/{lib,src,autoload.inc.php,LICENSE.LGPL}
+rm dompdf-0.7.0.tar.gz
 mv LICENSE.LGPL LICENSE
 
 curl https://codeload.github.com/PhenX/php-font-lib/tar.gz/0.4 -o php-font-lib-0.4.tar.gz
-mkdir -p lib/php-font-lib/classes
-tar xzf php-font-lib-0.4.tar.gz --strip-components 3 -C lib/php-font-lib/classes php-font-lib-0.4/src/FontLib
+mkdir -p lib/php-font-lib
+tar xzf php-font-lib-0.4.tar.gz --strip-components 1 -C lib/php-font-lib php-font-lib-0.4/{src,LICENSE}
 rm php-font-lib-0.4.tar.gz
+
+curl https://codeload.github.com/PhenX/php-svg-lib/tar.gz/v0.1 -o php-svg-lib-0.1.tar.gz
+mkdir -p lib/php-svg-lib
+tar xzf php-svg-lib-0.1.tar.gz --strip-components 1 -C lib/php-svg-lib php-svg-lib-0.1/src
+rm php-svg-lib-0.1.tar.gz
diff --git a/library/vendor/dompdf/autoload.inc.php b/library/vendor/dompdf/autoload.inc.php
new file mode 100644
index 000000000..c5c32f193
--- /dev/null
+++ b/library/vendor/dompdf/autoload.inc.php
@@ -0,0 +1,28 @@
+
+ * @author  Fabien Ménager 
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+/**
+ * Dompdf autoload function
+ *
+ * If you have an existing autoload function, add a call to this function
+ * from your existing __autoload() implementation.
+ *
+ * @param string $class
+ */
+
+require_once __DIR__ . '/lib/html5lib/Parser.php';
+require_once __DIR__ . '/lib/php-font-lib/src/FontLib/Autoloader.php';
+require_once __DIR__ . '/lib/php-svg-lib/src/autoload.php';
+
+/*
+ * New PHP 5.3.0 namespaced autoloader
+ */
+require_once __DIR__ . '/src/Autoloader.php';
+
+Dompdf\Autoloader::register();
diff --git a/library/vendor/dompdf/dompdf.php b/library/vendor/dompdf/dompdf.php
deleted file mode 100644
index dc3551b73..000000000
--- a/library/vendor/dompdf/dompdf.php
+++ /dev/null
@@ -1,279 +0,0 @@
-
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-
-/**
- * Display command line usage
- */
-function dompdf_usage() {
-  $default_paper_size = DOMPDF_DEFAULT_PAPER_SIZE;
-  
-  echo <<set_option('enable_php', false);
-  
-  if ( isset($_GET["input_file"]) )
-    $file = rawurldecode($_GET["input_file"]);
-  else
-    throw new DOMPDF_Exception("An input file is required (i.e. input_file _GET variable).");
-  
-  if ( isset($_GET["paper"]) )
-    $paper = rawurldecode($_GET["paper"]);
-  else
-    $paper = DOMPDF_DEFAULT_PAPER_SIZE;
-  
-  if ( isset($_GET["orientation"]) )
-    $orientation = rawurldecode($_GET["orientation"]);
-  else
-    $orientation = "portrait";
-  
-  if ( isset($_GET["base_path"]) ) {
-    $base_path = rawurldecode($_GET["base_path"]);
-    $file = $base_path . $file; # Set the input file
-  }  
-  
-  if ( isset($_GET["options"]) ) {
-    $options = $_GET["options"];
-  }
-  
-  $file_parts = explode_url($file);
-  
-  $outfile = "dompdf_out.pdf"; # Don't allow them to set the output file
-  $save_file = false; # Don't save the file
-  
-  break;
-}
-
-if ( $file === "-" ) {
-  $str = "";
-  while ( !feof(STDIN) )
-    $str .= fread(STDIN, 4096);
-
-  $dompdf->load_html($str);
-
-} else
-  $dompdf->load_html_file($file);
-
-if ( isset($base_path) ) {
-  $dompdf->set_base_path($base_path);
-}
-
-$dompdf->set_paper($paper, $orientation);
-
-$dompdf->render();
-
-if ( $_dompdf_show_warnings ) {
-  global $_dompdf_warnings;
-  foreach ($_dompdf_warnings as $msg)
-    echo $msg . "\n";
-  echo $dompdf->get_canvas()->get_cpdf()->messages;
-  flush();
-}
-
-if ( $save_file ) {
-//   if ( !is_writable($outfile) )
-//     throw new DOMPDF_Exception("'$outfile' is not writable.");
-  if ( strtolower(DOMPDF_PDF_BACKEND) === "gd" )
-    $outfile = str_replace(".pdf", ".png", $outfile);
-
-  list($proto, $host, $path, $file) = explode_url($outfile);
-  if ( $proto != "" ) // i.e. not file://
-    $outfile = $file; // just save it locally, FIXME? could save it like wget: ./host/basepath/file
-
-  $outfile = realpath(dirname($outfile)) . DIRECTORY_SEPARATOR . basename($outfile);
-
-  if ( strpos($outfile, DOMPDF_CHROOT) !== 0 )
-    throw new DOMPDF_Exception("Permission denied.");
-
-  file_put_contents($outfile, $dompdf->output( array("compress" => 0) ));
-  exit(0);
-}
-
-if ( !headers_sent() ) {
-  $dompdf->stream($outfile, $options);
-}
diff --git a/library/vendor/dompdf/dompdf_config.custom.inc.php b/library/vendor/dompdf/dompdf_config.custom.inc.php
deleted file mode 100644
index e9c0899d5..000000000
--- a/library/vendor/dompdf/dompdf_config.custom.inc.php
+++ /dev/null
@@ -1,41 +0,0 @@
-
- * @author  Helmut Tischer 
- * @author  Fabien Ménager 
- * @author   Brian Sweeney 
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-
-if ( class_exists( 'DOMPDF' , false ) ) { return; }
-
-PHP_VERSION >= 5.0 or die("DOMPDF requires PHP 5.0+");
-
-/**
- * The root of your DOMPDF installation
- */
-define("DOMPDF_DIR", str_replace(DIRECTORY_SEPARATOR, '/', realpath(dirname(__FILE__))));
-
-/**
- * The location of the DOMPDF include directory
- */
-define("DOMPDF_INC_DIR", DOMPDF_DIR . "/include");
-
-/**
- * The location of the DOMPDF lib directory
- */
-define("DOMPDF_LIB_DIR", DOMPDF_DIR . "/lib");
-
-/**
- * Some installations don't have $_SERVER['DOCUMENT_ROOT']
- * http://fyneworks.blogspot.com/2007/08/php-documentroot-in-iis-windows-servers.html
- */
-if( !isset($_SERVER['DOCUMENT_ROOT']) ) {
-  $path = "";
-  
-  if ( isset($_SERVER['SCRIPT_FILENAME']) )
-    $path = $_SERVER['SCRIPT_FILENAME'];
-  elseif ( isset($_SERVER['PATH_TRANSLATED']) )
-    $path = str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']);
-    
-  $_SERVER['DOCUMENT_ROOT'] = str_replace( '\\', '/', substr($path, 0, 0-strlen($_SERVER['PHP_SELF'])));
-}
-
-/** Include the custom config file if it exists */
-if ( file_exists(DOMPDF_DIR . "/dompdf_config.custom.inc.php") ){
-  require_once(DOMPDF_DIR . "/dompdf_config.custom.inc.php");
-}
-
-//FIXME: Some function definitions rely on the constants defined by DOMPDF. However, might this location prove problematic?
-require_once(DOMPDF_INC_DIR . "/functions.inc.php");
-
-/**
- * Username and password used by the configuration utility in www/
- */
-def("DOMPDF_ADMIN_USERNAME", "user");
-def("DOMPDF_ADMIN_PASSWORD", "password");
-
-/**
- * The location of the DOMPDF font directory
- *
- * The location of the directory where DOMPDF will store fonts and font metrics
- * Note: This directory must exist and be writable by the webserver process.
- * *Please note the trailing slash.*
- *
- * Notes regarding fonts:
- * Additional .afm font metrics can be added by executing load_font.php from command line.
- *
- * Only the original "Base 14 fonts" are present on all pdf viewers. Additional fonts must
- * be embedded in the pdf file or the PDF may not display correctly. This can significantly
- * increase file size unless font subsetting is enabled. Before embedding a font please
- * review your rights under the font license.
- *
- * Any font specification in the source HTML is translated to the closest font available
- * in the font directory.
- *
- * The pdf standard "Base 14 fonts" are:
- * Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique,
- * Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique,
- * Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic,
- * Symbol, ZapfDingbats.
- */
-def("DOMPDF_FONT_DIR", DOMPDF_DIR . "/lib/fonts/");
-
-/**
- * The location of the DOMPDF font cache directory
- *
- * This directory contains the cached font metrics for the fonts used by DOMPDF.
- * This directory can be the same as DOMPDF_FONT_DIR
- * 
- * Note: This directory must exist and be writable by the webserver process.
- */
-def("DOMPDF_FONT_CACHE", DOMPDF_FONT_DIR);
-
-/**
- * The location of a temporary directory.
- *
- * The directory specified must be writeable by the webserver process.
- * The temporary directory is required to download remote images and when
- * using the PFDLib back end.
- */
-def("DOMPDF_TEMP_DIR", sys_get_temp_dir());
-
-/**
- * ==== IMPORTANT ====
- *
- * dompdf's "chroot": Prevents dompdf from accessing system files or other
- * files on the webserver.  All local files opened by dompdf must be in a
- * subdirectory of this directory.  DO NOT set it to '/' since this could
- * allow an attacker to use dompdf to read any files on the server.  This
- * should be an absolute path.
- * This is only checked on command line call by dompdf.php, but not by
- * direct class use like:
- * $dompdf = new DOMPDF();	$dompdf->load_html($htmldata); $dompdf->render(); $pdfdata = $dompdf->output();
- */
-def("DOMPDF_CHROOT", realpath(DOMPDF_DIR));
-
-/**
- * Whether to use Unicode fonts or not.
- *
- * When set to true the PDF backend must be set to "CPDF" and fonts must be
- * loaded via load_font.php.
- *
- * When enabled, dompdf can support all Unicode glyphs. Any glyphs used in a
- * document must be present in your fonts, however.
- */
-def("DOMPDF_UNICODE_ENABLED", true);
-
-/**
- * Whether to enable font subsetting or not.
- */
-def("DOMPDF_ENABLE_FONTSUBSETTING", false);
-
-/**
- * The PDF rendering backend to use
- *
- * Valid settings are 'PDFLib', 'CPDF' (the bundled R&OS PDF class), 'GD' and
- * 'auto'. 'auto' will look for PDFLib and use it if found, or if not it will
- * fall back on CPDF. 'GD' renders PDFs to graphic files. {@link
- * Canvas_Factory} ultimately determines which rendering class to instantiate
- * based on this setting.
- *
- * Both PDFLib & CPDF rendering backends provide sufficient rendering
- * capabilities for dompdf, however additional features (e.g. object,
- * image and font support, etc.) differ between backends.  Please see
- * {@link PDFLib_Adapter} for more information on the PDFLib backend
- * and {@link CPDF_Adapter} and lib/class.pdf.php for more information
- * on CPDF. Also see the documentation for each backend at the links
- * below.
- *
- * The GD rendering backend is a little different than PDFLib and
- * CPDF. Several features of CPDF and PDFLib are not supported or do
- * not make any sense when creating image files.  For example,
- * multiple pages are not supported, nor are PDF 'objects'.  Have a
- * look at {@link GD_Adapter} for more information.  GD support is
- * experimental, so use it at your own risk.
- *
- * @link http://www.pdflib.com
- * @link http://www.ros.co.nz/pdf
- * @link http://www.php.net/image
- */
-def("DOMPDF_PDF_BACKEND", "CPDF");
-
-/**
- * PDFlib license key
- *
- * If you are using a licensed, commercial version of PDFlib, specify
- * your license key here.  If you are using PDFlib-Lite or are evaluating
- * the commercial version of PDFlib, comment out this setting.
- *
- * @link http://www.pdflib.com
- *
- * If pdflib present in web server and auto or selected explicitely above,
- * a real license code must exist!
- */
-//def("DOMPDF_PDFLIB_LICENSE", "your license key here");
-
-/**
- * html target media view which should be rendered into pdf.
- * List of types and parsing rules for future extensions:
- * http://www.w3.org/TR/REC-html40/types.html
- *   screen, tty, tv, projection, handheld, print, braille, aural, all
- * Note: aural is deprecated in CSS 2.1 because it is replaced by speech in CSS 3.
- * Note, even though the generated pdf file is intended for print output,
- * the desired content might be different (e.g. screen or projection view of html file).
- * Therefore allow specification of content here.
- */
-def("DOMPDF_DEFAULT_MEDIA_TYPE", "screen");
-
-/**
- * The default paper size.
- *
- * North America standard is "letter"; other countries generally "a4"
- *
- * @see CPDF_Adapter::PAPER_SIZES for valid sizes
- */
-def("DOMPDF_DEFAULT_PAPER_SIZE", "letter");
-
-/**
- * The default font family
- *
- * Used if no suitable fonts can be found. This must exist in the font folder.
- * @var string
- */
-def("DOMPDF_DEFAULT_FONT", "serif");
-
-/**
- * Image DPI setting
- *
- * This setting determines the default DPI setting for images and fonts.  The
- * DPI may be overridden for inline images by explictly setting the
- * image's width & height style attributes (i.e. if the image's native
- * width is 600 pixels and you specify the image's width as 72 points,
- * the image will have a DPI of 600 in the rendered PDF.  The DPI of
- * background images can not be overridden and is controlled entirely
- * via this parameter.
- *
- * For the purposes of DOMPDF, pixels per inch (PPI) = dots per inch (DPI).
- * If a size in html is given as px (or without unit as image size),
- * this tells the corresponding size in pt at 72 DPI.
- * This adjusts the relative sizes to be similar to the rendering of the
- * html page in a reference browser.
- *
- * In pdf, always 1 pt = 1/72 inch
- *
- * Rendering resolution of various browsers in px per inch:
- * Windows Firefox and Internet Explorer:
- *   SystemControl->Display properties->FontResolution: Default:96, largefonts:120, custom:?
- * Linux Firefox:
- *   about:config *resolution: Default:96
- *   (xorg screen dimension in mm and Desktop font dpi settings are ignored)
- *
- * Take care about extra font/image zoom factor of browser.
- *
- * In images,  size in pixel attribute, img css style, are overriding
- * the real image dimension in px for rendering.
- *
- * @var int
- */
-def("DOMPDF_DPI", 96);
-
-/**
- * Enable inline PHP
- *
- * If this setting is set to true then DOMPDF will automatically evaluate
- * inline PHP contained within  tags.
- *
- * Attention!
- * Enabling this for documents you do not trust (e.g. arbitrary remote html
- * pages) is a security risk. Inline scripts are run with the same level of
- * system access available to dompdf. Set this option to false (recommended)
- * if you wish to process untrusted documents.
- *
- * @var bool
- */
-def("DOMPDF_ENABLE_PHP", false);
-
-/**
- * Enable inline Javascript
- *
- * If this setting is set to true then DOMPDF will automatically insert
- * JavaScript code contained within  tags.
- *
- * @var bool
- */
-def("DOMPDF_ENABLE_JAVASCRIPT", true);
-
-/**
- * Enable remote file access
- *
- * If this setting is set to true, DOMPDF will access remote sites for
- * images and CSS files as required.
- * This is required for part of test case www/test/image_variants.html through www/examples.php
- *
- * Attention!
- * This can be a security risk, in particular in combination with DOMPDF_ENABLE_PHP and
- * allowing remote access to dompdf.php or on allowing remote html code to be passed to
- * $dompdf = new DOMPDF(); $dompdf->load_html(...);
- * This allows anonymous users to download legally doubtful internet content which on
- * tracing back appears to being downloaded by your server, or allows malicious php code
- * in remote html pages to be executed by your server with your account privileges.
- *
- * @var bool
- */
-def("DOMPDF_ENABLE_REMOTE", false);
-
-/**
- * The debug output log
- * @var string
- */
-def("DOMPDF_LOG_OUTPUT_FILE", DOMPDF_FONT_DIR."log.htm");
-
-/**
- * A ratio applied to the fonts height to be more like browsers' line height
- */
-def("DOMPDF_FONT_HEIGHT_RATIO", 1.1);
-
-/**
- * Enable CSS float
- *
- * Allows people to disabled CSS float support
- * @var bool
- */
-def("DOMPDF_ENABLE_CSS_FLOAT", false);
-
-/**
- * Enable the built in DOMPDF autoloader
- *
- * @var bool
- */
-def("DOMPDF_ENABLE_AUTOLOAD", true);
-
-/**
- * Prepend the DOMPDF autoload function to the spl_autoload stack
- *
- * @var bool
- */
-def("DOMPDF_AUTOLOAD_PREPEND", false);
-
-/**
- * Use the more-than-experimental HTML5 Lib parser
- */
-def("DOMPDF_ENABLE_HTML5PARSER", false);
-require_once(DOMPDF_LIB_DIR . "/html5lib/Parser.php");
-
-// ### End of user-configurable options ###
-
-/**
- * Load autoloader
- */
-if (DOMPDF_ENABLE_AUTOLOAD) {
-  require_once(DOMPDF_INC_DIR . "/autoload.inc.php");
-  require_once(DOMPDF_LIB_DIR . "/php-font-lib/classes/Font.php");
-}
-
-/**
- * Ensure that PHP is working with text internally using UTF8 character encoding.
- */
-mb_internal_encoding('UTF-8');
-
-/**
- * Global array of warnings generated by DomDocument parser and
- * stylesheet class
- *
- * @var array
- */
-global $_dompdf_warnings;
-$_dompdf_warnings = array();
-
-/**
- * If true, $_dompdf_warnings is dumped on script termination when using
- * dompdf/dompdf.php or after rendering when using the DOMPDF class.
- * When using the class, setting this value to true will prevent you from
- * streaming the PDF.
- *
- * @var bool
- */
-global $_dompdf_show_warnings;
-$_dompdf_show_warnings = false;
-
-/**
- * If true, the entire tree is dumped to stdout in dompdf.cls.php.
- * Setting this value to true will prevent you from streaming the PDF.
- *
- * @var bool
- */
-global $_dompdf_debug;
-$_dompdf_debug = false;
-
-/**
- * Array of enabled debug message types
- *
- * @var array
- */
-global $_DOMPDF_DEBUG_TYPES;
-$_DOMPDF_DEBUG_TYPES = array(); //array("page-break" => 1);
-
-/* Optionally enable different classes of debug output before the pdf content.
- * Visible if displaying pdf as text,
- * E.g. on repeated display of same pdf in browser when pdf is not taken out of
- * the browser cache and the premature output prevents setting of the mime type.
- */
-def('DEBUGPNG', false);
-def('DEBUGKEEPTEMP', false);
-def('DEBUGCSS', false);
-
-/* Layout debugging. Will display rectangles around different block levels.
- * Visible in the PDF itself.
- */
-def('DEBUG_LAYOUT', false);
-def('DEBUG_LAYOUT_LINES', true);
-def('DEBUG_LAYOUT_BLOCKS', true);
-def('DEBUG_LAYOUT_INLINE', true);
-def('DEBUG_LAYOUT_PADDINGBOX', true);
diff --git a/library/vendor/dompdf/include/absolute_positioner.cls.php b/library/vendor/dompdf/include/absolute_positioner.cls.php
deleted file mode 100644
index 050db49f7..000000000
--- a/library/vendor/dompdf/include/absolute_positioner.cls.php
+++ /dev/null
@@ -1,125 +0,0 @@
-
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-
-/**
- * Positions absolutely positioned frames
- */
-class Absolute_Positioner extends Positioner {
-
-  function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
-
-  function position() {
-
-    $frame = $this->_frame;
-    $style = $frame->get_style();
-    
-    $p = $frame->find_positionned_parent();
-    
-    list($x, $y, $w, $h) = $frame->get_containing_block();
-
-    $top    = $style->length_in_pt($style->top,    $h);
-    $right  = $style->length_in_pt($style->right,  $w);
-    $bottom = $style->length_in_pt($style->bottom, $h);
-    $left   = $style->length_in_pt($style->left,   $w);
-    
-    if ( $p && !($left === "auto" && $right === "auto") ) {
-      // Get the parent's padding box (see http://www.w3.org/TR/CSS21/visuren.html#propdef-top)
-      list($x, $y, $w, $h) = $p->get_padding_box();
-    }
-    
-    list($width, $height) = array($frame->get_margin_width(), $frame->get_margin_height());
-    
-    $orig_style = $this->_frame->get_original_style();
-    $orig_width = $orig_style->width;
-    $orig_height = $orig_style->height;
-    
-    /****************************
-    
-    Width auto: 
-    ____________| left=auto | left=fixed |
-    right=auto  |     A     |     B      |
-    right=fixed |     C     |     D      |
-    
-    Width fixed: 
-    ____________| left=auto | left=fixed |
-    right=auto  |     E     |     F      |
-    right=fixed |     G     |     H      |
-    
-    *****************************/
-    
-    if ( $left === "auto" ) {
-      if ( $right === "auto" ) {
-        // A or E - Keep the frame at the same position
-        $x = $x + $frame->find_block_parent()->get_current_line_box()->w;
-      }
-      else {
-        if ( $orig_width === "auto" ) {
-          // C
-          $x += $w - $width - $right;
-        }
-        else {
-          // G
-          $x += $w - $width - $right;
-        }
-      }
-    }
-    else {
-      if ( $right === "auto" ) {
-        // B or F
-        $x += $left;
-      }
-      else {
-        if ( $orig_width === "auto" ) {
-          // D - TODO change width
-          $x += $left;
-        }
-        else {
-          // H - Everything is fixed: left + width win
-          $x += $left;
-        }
-      }
-    }
-    
-    // The same vertically
-    if ( $top === "auto" ) {
-      if ( $bottom === "auto" ) {
-        // A or E - Keep the frame at the same position
-        $y = $frame->find_block_parent()->get_current_line_box()->y;
-      }
-      else {
-        if ( $orig_height === "auto" ) {
-          // C
-          $y += $h - $height - $bottom;
-        }
-        else {
-          // G
-          $y += $h - $height - $bottom;
-        }
-      }
-    }
-    else {
-      if ( $bottom === "auto" ) {
-        // B or F
-        $y += $top;
-      }
-      else {
-        if ( $orig_height === "auto" ) {
-          // D - TODO change height
-          $y += $top;
-        }
-        else {
-          // H - Everything is fixed: top + height win
-          $y += $top;
-        }
-      }
-    }
-    
-    $frame->set_position($x, $y);
-
-  }
-}
\ No newline at end of file
diff --git a/library/vendor/dompdf/include/abstract_renderer.cls.php b/library/vendor/dompdf/include/abstract_renderer.cls.php
deleted file mode 100644
index dcd801306..000000000
--- a/library/vendor/dompdf/include/abstract_renderer.cls.php
+++ /dev/null
@@ -1,759 +0,0 @@
-
- * @author  Helmut Tischer 
- * @author  Fabien Ménager 
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-
-/**
- * Base renderer class
- *
- * @access private
- * @package dompdf
- */
-abstract class Abstract_Renderer {
-
-  /**
-   * Rendering backend
-   *
-   * @var Canvas
-   */
-  protected $_canvas;
-
-  /**
-   * Current dompdf instance
-   *
-   * @var DOMPDF
-   */
-  protected $_dompdf;
-  
-  /**
-   * Class constructor
-   *
-   * @param DOMPDF $dompdf The current dompdf instance
-   */
-  function __construct(DOMPDF $dompdf) {
-    $this->_dompdf = $dompdf;
-    $this->_canvas = $dompdf->get_canvas();
-  }
-  
-  /**
-   * Render a frame.
-   *
-   * Specialized in child classes
-   *
-   * @param Frame $frame The frame to render
-   */
-  abstract function render(Frame $frame);
-
-  //........................................................................
-
-  /**
-   * Render a background image over a rectangular area
-   *
-   * @param string $url      The background image to load
-   * @param float  $x        The left edge of the rectangular area
-   * @param float  $y        The top edge of the rectangular area
-   * @param float  $width    The width of the rectangular area
-   * @param float  $height   The height of the rectangular area
-   * @param Style  $style    The associated Style object
-   *
-   * @throws Exception
-   */
-  protected function _background_image($url, $x, $y, $width, $height, $style) {
-    if ( !function_exists("imagecreatetruecolor") ) {
-      throw new Exception("The PHP GD extension is required, but is not installed.");
-    }
-
-    $sheet = $style->get_stylesheet();
-
-    // Skip degenerate cases
-    if ( $width == 0 || $height == 0 ) {
-      return;
-    }
-    
-    $box_width = $width;
-    $box_height = $height;
-
-    //debugpng
-    if (DEBUGPNG) print '[_background_image '.$url.']';
-
-    list($img, $type, /*$msg*/) = Image_Cache::resolve_url(
-      $url,
-      $sheet->get_protocol(),
-      $sheet->get_host(),
-      $sheet->get_base_path(),
-      $this->_dompdf
-    );
-
-    // Bail if the image is no good
-    if ( Image_Cache::is_broken($img) ) {
-      return;
-    }
-
-    //Try to optimize away reading and composing of same background multiple times
-    //Postponing read with imagecreatefrom   ...()
-    //final composition parameters and name not known yet
-    //Therefore read dimension directly from file, instead of creating gd object first.
-    //$img_w = imagesx($src); $img_h = imagesy($src);
-
-    list($img_w, $img_h) = dompdf_getimagesize($img, $this->_dompdf->get_http_context());
-    if (!isset($img_w) || $img_w == 0 || !isset($img_h) || $img_h == 0) {
-      return;
-    }
-
-    $repeat = $style->background_repeat;
-    $dpi = $this->_dompdf->get_option("dpi");
-
-    //Increase background resolution and dependent box size according to image resolution to be placed in
-    //Then image can be copied in without resize
-    $bg_width = round((float)($width * $dpi) / 72);
-    $bg_height = round((float)($height * $dpi) / 72);
-
-    //Need %bg_x, $bg_y as background pos, where img starts, converted to pixel
-
-    list($bg_x, $bg_y) = $style->background_position;
-
-    if ( is_percent($bg_x) ) {
-      // The point $bg_x % from the left edge of the image is placed
-      // $bg_x % from the left edge of the background rectangle
-      $p = ((float)$bg_x)/100.0;
-      $x1 = $p * $img_w;
-      $x2 = $p * $bg_width;
-
-      $bg_x = $x2 - $x1;
-    }
-    else {
-      $bg_x = (float)($style->length_in_pt($bg_x)*$dpi) / 72;
-    }
-    
-    $bg_x = round($bg_x + $style->length_in_pt($style->border_left_width)*$dpi / 72);
-
-    if ( is_percent($bg_y) ) {
-      // The point $bg_y % from the left edge of the image is placed
-      // $bg_y % from the left edge of the background rectangle
-      $p = ((float)$bg_y)/100.0;
-      $y1 = $p * $img_h;
-      $y2 = $p * $bg_height;
-
-      $bg_y = $y2 - $y1;
-    }
-    else {
-      $bg_y = (float)($style->length_in_pt($bg_y)*$dpi) / 72;
-    }
-    
-    $bg_y = round($bg_y + $style->length_in_pt($style->border_top_width)*$dpi / 72);
-
-    //clip background to the image area on partial repeat. Nothing to do if img off area
-    //On repeat, normalize start position to the tile at immediate left/top or 0/0 of area
-    //On no repeat with positive offset: move size/start to have offset==0
-    //Handle x/y Dimensions separately
-
-    if ( $repeat !== "repeat" && $repeat !== "repeat-x" ) {
-      //No repeat x
-      if ($bg_x < 0) {
-        $bg_width = $img_w + $bg_x;
-      }
-      else {
-        $x += ($bg_x * 72)/$dpi;
-        $bg_width = $bg_width - $bg_x;
-        if ($bg_width > $img_w) {
-          $bg_width = $img_w;
-        }
-        $bg_x = 0;
-      }
-      
-      if ($bg_width <= 0) {
-        return;
-      }
-      
-      $width = (float)($bg_width * 72)/$dpi;
-    }
-    else {
-      //repeat x
-      if ($bg_x < 0) {
-        $bg_x = - ((-$bg_x) % $img_w);
-      }
-      else {
-        $bg_x = $bg_x % $img_w;
-        if ($bg_x > 0) {
-          $bg_x -= $img_w;
-        }
-      }
-    }
-
-    if ( $repeat !== "repeat" && $repeat !== "repeat-y" ) {
-      //no repeat y
-      if ($bg_y < 0) {
-        $bg_height = $img_h + $bg_y;
-      }
-      else {
-        $y += ($bg_y * 72)/$dpi;
-        $bg_height = $bg_height - $bg_y;
-        if ($bg_height > $img_h) {
-          $bg_height = $img_h;
-        }
-        $bg_y = 0;
-      }
-      if ($bg_height <= 0) {
-        return;
-      }
-      $height = (float)($bg_height * 72)/$dpi;
-    }
-    else {
-      //repeat y
-      if ($bg_y < 0) {
-        $bg_y = - ((-$bg_y) % $img_h);
-      }
-      else {
-        $bg_y = $bg_y % $img_h;
-        if ($bg_y > 0) {
-          $bg_y -= $img_h;
-        }
-      }
-    }
-
-    //Optimization, if repeat has no effect
-    if ( $repeat === "repeat" && $bg_y <= 0 && $img_h+$bg_y >= $bg_height ) {
-      $repeat = "repeat-x";
-    }
-    
-    if ( $repeat === "repeat" && $bg_x <= 0 && $img_w+$bg_x >= $bg_width ) {
-      $repeat = "repeat-y";
-    }
-    
-    if ( ($repeat === "repeat-x" && $bg_x <= 0 && $img_w+$bg_x >= $bg_width) ||
-         ($repeat === "repeat-y" && $bg_y <= 0 && $img_h+$bg_y >= $bg_height) ) {
-      $repeat = "no-repeat";
-    }
-
-    //Use filename as indicator only
-    //different names for different variants to have different copies in the pdf
-    //This is not dependent of background color of box! .'_'.(is_array($bg_color) ? $bg_color["hex"] : $bg_color)
-    //Note: Here, bg_* are the start values, not end values after going through the tile loops!
-
-    $filedummy = $img;
-    
-    $is_png = false;
-    $filedummy .= '_'.$bg_width.'_'.$bg_height.'_'.$bg_x.'_'.$bg_y.'_'.$repeat;
-
-    //Optimization to avoid multiple times rendering the same image.
-    //If check functions are existing and identical image already cached,
-    //then skip creation of duplicate, because it is not needed by addImagePng
-    if ( $this->_canvas instanceof CPDF_Adapter &&
-         $this->_canvas->get_cpdf()->image_iscached($filedummy) ) {
-      $bg = null;
-    }
-    
-    else {
-  
-      // Create a new image to fit over the background rectangle
-      $bg = imagecreatetruecolor($bg_width, $bg_height);
-      
-      switch (strtolower($type)) {
-        case IMAGETYPE_PNG:
-          $is_png = true;
-          imagesavealpha($bg, true);
-          imagealphablending($bg, false);
-          $src = imagecreatefrompng($img);
-          break;
-    
-        case IMAGETYPE_JPEG:
-          $src = imagecreatefromjpeg($img);
-          break;
-    
-        case IMAGETYPE_GIF:
-          $src = imagecreatefromgif($img);
-          break;
-          
-        case IMAGETYPE_BMP:
-          $src = imagecreatefrombmp($img);
-          break;
-    
-        default:
-          return; // Unsupported image type
-      }
-  
-      if ( $src == null ) {
-        return;
-      }
-  
-      //Background color if box is not relevant here
-      //Non transparent image: box clipped to real size. Background non relevant.
-      //Transparent image: The image controls the transparency and lets shine through whatever background.
-      //However on transparent image preset the composed image with the transparency color,
-      //to keep the transparency when copying over the non transparent parts of the tiles.
-      $ti = imagecolortransparent($src);
-      
-      if ( $ti >= 0 ) {
-        $tc = imagecolorsforindex($src, $ti);
-        $ti = imagecolorallocate($bg, $tc['red'], $tc['green'], $tc['blue']);
-        imagefill($bg, 0, 0, $ti);
-        imagecolortransparent($bg, $ti);
-      }
-  
-      //This has only an effect for the non repeatable dimension.
-      //compute start of src and dest coordinates of the single copy
-      if ( $bg_x < 0 ) {
-        $dst_x = 0;
-        $src_x = -$bg_x;
-      }
-      else {
-        $src_x = 0;
-        $dst_x = $bg_x;
-      }
-  
-      if ( $bg_y < 0 ) {
-        $dst_y = 0;
-        $src_y = -$bg_y;
-      }
-      else {
-        $src_y = 0;
-        $dst_y = $bg_y;
-      }
-  
-      //For historical reasons exchange meanings of variables:
-      //start_* will be the start values, while bg_* will be the temporary start values in the loops
-      $start_x = $bg_x;
-      $start_y = $bg_y;
-  
-      // Copy regions from the source image to the background
-      if ( $repeat === "no-repeat" ) {
-  
-        // Simply place the image on the background
-        imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $img_h);
-  
-      }
-      else if ( $repeat === "repeat-x" ) {
-  
-        for ( $bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w ) {
-          if ( $bg_x < 0 ) {
-            $dst_x = 0;
-            $src_x = -$bg_x;
-            $w = $img_w + $bg_x;
-          }
-          else {
-            $dst_x = $bg_x;
-            $src_x = 0;
-            $w = $img_w;
-          }
-          imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $img_h);
-        }
-  
-      }
-      else if ( $repeat === "repeat-y" ) {
-  
-        for ( $bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h ) {
-          if ( $bg_y < 0 ) {
-            $dst_y = 0;
-            $src_y = -$bg_y;
-            $h = $img_h + $bg_y;
-          }
-          else {
-            $dst_y = $bg_y;
-            $src_y = 0;
-            $h = $img_h;
-          }
-          imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $h);
-  
-        }
-  
-      }
-      else if ( $repeat === "repeat" ) {
-  
-        for ( $bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h ) {
-          for ( $bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w ) {
-  
-            if ( $bg_x < 0 ) {
-              $dst_x = 0;
-              $src_x = -$bg_x;
-              $w = $img_w + $bg_x;
-            }
-            else {
-              $dst_x = $bg_x;
-              $src_x = 0;
-              $w = $img_w;
-            }
-  
-            if ( $bg_y < 0 ) {
-              $dst_y = 0;
-              $src_y = -$bg_y;
-              $h = $img_h + $bg_y;
-            }
-            else {
-              $dst_y = $bg_y;
-              $src_y = 0;
-              $h = $img_h;
-            }
-            imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $h);
-          }
-        }
-      }
-      
-      else {
-        print 'Unknown repeat!';
-      }
-      
-      imagedestroy($src);
-
-    } /* End optimize away creation of duplicates */
-
-    $this->_canvas->clipping_rectangle($x, $y, $box_width, $box_height);
-    
-    //img: image url string
-    //img_w, img_h: original image size in px
-    //width, height: box size in pt
-    //bg_width, bg_height: box size in px
-    //x, y: left/top edge of box on page in pt
-    //start_x, start_y: placement of image relative to pattern
-    //$repeat: repeat mode
-    //$bg: GD object of result image
-    //$src: GD object of original image
-    //When using cpdf and optimization to direct png creation from gd object is available,
-    //don't create temp file, but place gd object directly into the pdf
-    if ( !$is_png && $this->_canvas instanceof CPDF_Adapter ) {
-      // Note: CPDF_Adapter image converts y position
-      $this->_canvas->get_cpdf()->addImagePng($filedummy, $x, $this->_canvas->get_height() - $y - $height, $width, $height, $bg);
-    } 
-    
-    else {
-      $tmp_dir = $this->_dompdf->get_option("temp_dir");
-      $tmp_name = tempnam($tmp_dir, "bg_dompdf_img_");
-      @unlink($tmp_name);
-      $tmp_file = "$tmp_name.png";
-      
-      //debugpng
-      if (DEBUGPNG) print '[_background_image '.$tmp_file.']';
-
-      imagepng($bg, $tmp_file);
-      $this->_canvas->image($tmp_file, $x, $y, $width, $height);
-      imagedestroy($bg);
-
-      //debugpng
-      if (DEBUGPNG) print '[_background_image unlink '.$tmp_file.']';
-
-      if (!DEBUGKEEPTEMP) {
-        unlink($tmp_file);
-      }
-    }
-    
-    $this->_canvas->clipping_end();
-  }
-  
-  protected function _get_dash_pattern($style, $width) {
-    $pattern = array();
-    
-    switch ($style) {
-      default:
-      /*case "solid":
-      case "double":
-      case "groove":
-      case "inset":
-      case "outset":
-      case "ridge":*/
-      case "none": break;
-      
-      case "dotted": 
-        if ( $width <= 1 )
-          $pattern = array($width, $width*2);
-        else
-          $pattern = array($width);
-      break;
-      
-      case "dashed": 
-        $pattern = array(3 * $width);
-      break;
-    }
-    
-    return $pattern;
-  }
-
-  protected function _border_none($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
-    return;
-  }
-  
-  protected function _border_hidden($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
-    return;
-  }
-  
-  // Border rendering functions
-
-  protected function _border_dotted($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
-    $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dotted", $r1, $r2);
-  }
-
-
-  protected function _border_dashed($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
-    $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dashed", $r1, $r2);
-  }
-
-
-  protected function _border_solid($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
-    // TODO: Solve rendering where one corner is beveled (radius == 0), one corner isn't.
-    if ( $corner_style !== "bevel" || $r1 > 0 || $r2 > 0 ) {
-      // do it the simple way
-      $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "solid", $r1, $r2);
-      return;
-    }
-
-    list($top, $right, $bottom, $left) = $widths;
-
-    // All this polygon business is for beveled corners...
-    switch ($side) {
-      case "top":
-        $points = array($x, $y,
-                        $x + $length, $y,
-                        $x + $length - $right, $y + $top,
-                        $x + $left, $y + $top);
-        $this->_canvas->polygon($points, $color, null, null, true);
-        break;
-  
-      case "bottom":
-        $points = array($x, $y,
-                        $x + $length, $y,
-                        $x + $length - $right, $y - $bottom,
-                        $x + $left, $y - $bottom);
-        $this->_canvas->polygon($points, $color, null, null, true);
-        break;
-  
-      case "left":
-        $points = array($x, $y,
-                        $x, $y + $length,
-                        $x + $left, $y + $length - $bottom,
-                        $x + $left, $y + $top);
-        $this->_canvas->polygon($points, $color, null, null, true);
-        break;
-  
-      case "right":
-        $points = array($x, $y,
-                        $x, $y + $length,
-                        $x - $right, $y + $length - $bottom,
-                        $x - $right, $y + $top);
-        $this->_canvas->polygon($points, $color, null, null, true);
-        break;
-  
-      default:
-        return;
-    }
-  }
-
-  protected function _apply_ratio($side, $ratio, $top, $right, $bottom, $left, &$x, &$y, &$length, &$r1, &$r2) {
-    switch ($side) {
-
-    case "top":
-      $r1 -= $left * $ratio;
-      $r2 -= $right * $ratio;
-      $x += $left * $ratio;
-      $y += $top * $ratio;
-      $length -= $left * $ratio + $right * $ratio;
-      break;
-
-    case "bottom":
-      $r1 -= $right * $ratio;
-      $r2 -= $left * $ratio;
-      $x += $left * $ratio;
-      $y -= $bottom * $ratio;
-      $length -= $left * $ratio + $right * $ratio;
-      break;
-
-    case "left":
-      $r1 -= $top * $ratio;
-      $r2 -= $bottom * $ratio;
-      $x += $left * $ratio;
-      $y += $top * $ratio;
-      $length -= $top * $ratio + $bottom * $ratio;
-      break;
-
-    case "right":
-      $r1 -= $bottom * $ratio;
-      $r2 -= $top * $ratio;
-      $x -= $right * $ratio;
-      $y += $top * $ratio;
-      $length -= $top * $ratio + $bottom * $ratio;
-      break;
-
-    default:
-      return;
-
-    }
-  }
-
-  protected function _border_double($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
-    list($top, $right, $bottom, $left) = $widths;
-
-    $third_widths = array($top / 3, $right / 3, $bottom / 3, $left / 3);
-
-    // draw the outer border
-    $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2);
-
-    $this->_apply_ratio($side, 2/3, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
-
-    $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2);
-  }
-
-  protected function _border_groove($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
-    list($top, $right, $bottom, $left) = $widths;
-
-    $half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2);
-
-    $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
-    
-    $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
-
-    $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
-
-  }
-
-  protected function _border_ridge($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
-    list($top, $right, $bottom, $left) = $widths;
-
-    $half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2);
-
-    $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
-    
-    $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
-
-    $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
-
-  }
-
-  protected function _tint($c) {
-    if ( !is_numeric($c) )
-      return $c;
-
-    return min(1, $c + 0.16);
-  }
-
-  protected function _shade($c) {
-    if ( !is_numeric($c) )
-      return $c;
-
-    return max(0, $c - 0.33);
-  }
-
-  protected function _border_inset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
-    switch ($side) {
-      case "top":
-      case "left":
-        $shade = array_map(array($this, "_shade"), $color);
-        $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2);
-        break;
-  
-      case "bottom":
-      case "right":
-        $tint = array_map(array($this, "_tint"), $color);
-        $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2);
-        break;
-  
-      default:
-        return;
-    }
-  }
-
-  protected function _border_outset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
-    switch ($side) {
-      case "top":
-      case "left":
-        $tint = array_map(array($this, "_tint"), $color);
-        $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2);
-        break;
-  
-      case "bottom":
-      case "right":
-        $shade = array_map(array($this, "_shade"), $color);
-        $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2);
-        break;
-  
-      default:
-        return;
-    }
-  }
-  // Draws a solid, dotted, or dashed line, observing the border radius
-  protected function _border_line($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $pattern_name, $r1 = 0, $r2 = 0) {
-    list($top, $right, $bottom, $left) = $widths;
-    
-    $width = $$side;
-    $pattern = $this->_get_dash_pattern($pattern_name, $width);
-
-    $half_width = $width/2;
-    $r1 -= $half_width;
-    $r2 -= $half_width;
-    $adjust = $r1/80;
-    $length -= $width; 
-    
-    switch ($side) {
-      case "top":
-        $x += $half_width; 
-        $y += $half_width;
-  
-        if ( $r1 > 0 ) {
-          $this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 90-$adjust, 135+$adjust, $color, $width, $pattern);
-        }
-  
-        $this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern);
-  
-        if ( $r2 > 0 ) {
-          $this->_canvas->arc($x + $length - $r2, $y + $r2, $r2, $r2, 45-$adjust, 90+$adjust, $color, $width, $pattern);
-        }
-        break;
-        
-      case "bottom":
-        $x += $half_width; 
-        $y -= $half_width;
-        
-        if ( $r1 > 0 ) {
-          $this->_canvas->arc($x + $r1, $y - $r1, $r1, $r1, 225-$adjust, 270+$adjust, $color, $width, $pattern);
-        }
-        
-        $this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern);
-        
-        if ( $r2 > 0 ) {
-          $this->_canvas->arc($x + $length - $r2, $y - $r2, $r2, $r2, 270-$adjust, 315+$adjust, $color, $width, $pattern);
-        }
-        break;
-        
-      case "left":
-        $y += $half_width; 
-        $x += $half_width;
-        
-        if ( $r1 > 0 ) {
-          $this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 135-$adjust, 180+$adjust, $color, $width, $pattern);
-        }
-        
-        $this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern);
-        
-        if ( $r2 > 0 ) {
-          $this->_canvas->arc($x + $r2, $y + $length - $r2, $r2, $r2, 180-$adjust, 225+$adjust, $color, $width, $pattern);
-        }
-        break;
-        
-      case "right":
-        $y += $half_width; 
-        $x -= $half_width;
-        
-        if ( $r1 > 0 ) {
-          $this->_canvas->arc($x - $r1, $y + $r1, $r1, $r1, 0-$adjust, 45+$adjust, $color, $width, $pattern);
-        }
-        
-        $this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern);
-        
-        if ( $r2 > 0 ) {
-          $this->_canvas->arc($x - $r2, $y + $length - $r2, $r2, $r2, 315-$adjust, 360+$adjust, $color, $width, $pattern);
-        }
-        break;
-    }
-  }
-
-  protected function _set_opacity($opacity) {
-    if ( is_numeric($opacity) && $opacity <= 1.0 && $opacity >= 0.0 ) {
-      $this->_canvas->set_opacity( $opacity );
-    }
-  }
-
-  protected function _debug_layout($box, $color = "red", $style = array()) {
-    $this->_canvas->rectangle($box[0], $box[1], $box[2], $box[3], CSS_Color::parse($color), 0.1, $style);
-  }
-}
diff --git a/library/vendor/dompdf/include/attribute_translator.cls.php b/library/vendor/dompdf/include/attribute_translator.cls.php
deleted file mode 100644
index 68da668c1..000000000
--- a/library/vendor/dompdf/include/attribute_translator.cls.php
+++ /dev/null
@@ -1,592 +0,0 @@
-
- * @author  Fabien Ménager 
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-
-/**
- * Translates HTML 4.0 attributes into CSS rules
- *
- * @package dompdf
- */
-class Attribute_Translator {
-  static $_style_attr = "_html_style_attribute";
-  
-  // Munged data originally from
-  // http://www.w3.org/TR/REC-html40/index/attributes.html
-  // http://www.cs.tut.fi/~jkorpela/html2css.html
-  static private $__ATTRIBUTE_LOOKUP = array(
-    //'caption' => array ( 'align' => '', ),
-    'img' => array(
-      'align' => array(
-        'bottom' => 'vertical-align: baseline;',
-        'middle' => 'vertical-align: middle;',
-        'top'    => 'vertical-align: top;',
-        'left'   => 'float: left;',
-        'right'  => 'float: right;'
-      ),
-      'border' => 'border: %0.2F px solid;',
-      'height' => 'height: %s px;',
-      'hspace' => 'padding-left: %1$0.2F px; padding-right: %1$0.2F px;',
-      'vspace' => 'padding-top: %1$0.2F px; padding-bottom: %1$0.2F px;',
-      'width'  => 'width: %s px;',
-    ),
-    'table' => array(
-      'align' => array(
-        'left'   => 'margin-left: 0; margin-right: auto;',
-        'center' => 'margin-left: auto; margin-right: auto;',
-        'right'  => 'margin-left: auto; margin-right: 0;'
-      ),
-      'bgcolor' => 'background-color: %s;',
-      'border' => '!set_table_border',
-      'cellpadding' => '!set_table_cellpadding',//'border-spacing: %0.2F; border-collapse: separate;',
-      'cellspacing' => '!set_table_cellspacing',
-      'frame' => array(
-        'void'   => 'border-style: none;',
-        'above'  => 'border-top-style: solid;',
-        'below'  => 'border-bottom-style: solid;',
-        'hsides' => 'border-left-style: solid; border-right-style: solid;',
-        'vsides' => 'border-top-style: solid; border-bottom-style: solid;',
-        'lhs'    => 'border-left-style: solid;',
-        'rhs'    => 'border-right-style: solid;',
-        'box'    => 'border-style: solid;',
-        'border' => 'border-style: solid;'
-      ),
-      'rules' => '!set_table_rules',
-      'width' => 'width: %s;',
-    ),
-    'hr' => array(
-      'align'   => '!set_hr_align', // Need to grab width to set 'left' & 'right' correctly
-      'noshade' => 'border-style: solid;',
-      'size'    => '!set_hr_size', //'border-width: %0.2F px;',
-      'width'   => 'width: %s;',
-    ),
-    'div' => array(
-      'align' => 'text-align: %s;',
-    ),
-    'h1' => array(
-      'align' => 'text-align: %s;',
-    ),
-    'h2' => array(
-      'align' => 'text-align: %s;',
-    ),
-    'h3' => array(
-      'align' => 'text-align: %s;',
-    ),
-    'h4' => array(
-      'align' => 'text-align: %s;',
-    ),
-    'h5' => array(
-      'align' => 'text-align: %s;',
-    ),
-    'h6' => array(
-      'align' => 'text-align: %s;',
-    ),
-    'p' => array(
-      'align' => 'text-align: %s;',
-    ),
-//    'col' => array(
-//      'align'  => '',
-//      'valign' => '',
-//    ),
-//    'colgroup' => array(
-//      'align'  => '',
-//      'valign' => '',
-//    ),
-    'tbody' => array(
-      'align'  => '!set_table_row_align',
-      'valign' => '!set_table_row_valign',
-    ),
-    'td' => array(
-      'align'   => 'text-align: %s;',
-      'bgcolor' => '!set_background_color',
-      'height'  => 'height: %s;',
-      'nowrap'  => 'white-space: nowrap;',
-      'valign'  => 'vertical-align: %s;',
-      'width'   => 'width: %s;',
-    ),
-    'tfoot' => array(
-      'align'   => '!set_table_row_align',
-      'valign'  => '!set_table_row_valign',
-    ),
-    'th' => array(
-      'align'   => 'text-align: %s;',
-      'bgcolor' => '!set_background_color',
-      'height'  => 'height: %s;',
-      'nowrap'  => 'white-space: nowrap;',
-      'valign'  => 'vertical-align: %s;',
-      'width'   => 'width: %s;',
-    ),
-    'thead' => array(
-      'align'   => '!set_table_row_align',
-      'valign'  => '!set_table_row_valign',
-    ),
-    'tr' => array(
-      'align'   => '!set_table_row_align',
-      'bgcolor' => '!set_table_row_bgcolor',
-      'valign'  => '!set_table_row_valign',
-    ),
-    'body' => array(
-      'background' => 'background-image: url(%s);',
-      'bgcolor'    => '!set_background_color',
-      'link'       => '!set_body_link',
-      'text'       => '!set_color',
-    ),
-    'br' => array(
-      'clear' => 'clear: %s;',
-    ),
-    'basefont' => array(
-      'color' => '!set_color',
-      'face'  => 'font-family: %s;',
-      'size'  => '!set_basefont_size',
-    ),
-    'font' => array(
-      'color' => '!set_color',
-      'face'  => 'font-family: %s;',
-      'size'  => '!set_font_size',
-    ),
-    'dir' => array(
-      'compact' => 'margin: 0.5em 0;',
-    ),
-    'dl' => array(
-      'compact' => 'margin: 0.5em 0;',
-    ),
-    'menu' => array(
-      'compact' => 'margin: 0.5em 0;',
-    ),
-    'ol' => array(
-      'compact' => 'margin: 0.5em 0;',
-      'start'   => 'counter-reset: -dompdf-default-counter %d;',
-      'type'    => 'list-style-type: %s;',
-    ),
-    'ul' => array(
-      'compact' => 'margin: 0.5em 0;',
-      'type'    => 'list-style-type: %s;',
-    ),
-    'li' => array(
-      'type'    => 'list-style-type: %s;',
-      'value'   => 'counter-reset: -dompdf-default-counter %d;',
-    ),
-    'pre' => array(
-      'width' => 'width: %s;',
-    ),
-  );
-  
-  static protected $_last_basefont_size = 3;
-  static protected $_font_size_lookup = array(
-    // For basefont support
-    -3 => "4pt", 
-    -2 => "5pt", 
-    -1 => "6pt", 
-     0 => "7pt", 
-    
-     1 => "8pt",
-     2 => "10pt",
-     3 => "12pt",
-     4 => "14pt",
-     5 => "18pt",
-     6 => "24pt",
-     7 => "34pt",
-     
-    // For basefont support
-     8 => "48pt", 
-     9 => "44pt", 
-    10 => "52pt", 
-    11 => "60pt", 
-  );
-
-  /**
-   * @param Frame $frame
-   */
-  static function translate_attributes(Frame $frame) {
-    $node = $frame->get_node();
-    $tag = $node->nodeName;
-
-    if ( !isset(self::$__ATTRIBUTE_LOOKUP[$tag]) ) {
-      return;
-    }
-
-    $valid_attrs = self::$__ATTRIBUTE_LOOKUP[$tag];
-    $attrs = $node->attributes;
-    $style = rtrim($node->getAttribute(self::$_style_attr), "; ");
-    if ( $style != "" ) {
-      $style .= ";";
-    }
-
-    foreach ($attrs as $attr => $attr_node ) {
-      if ( !isset($valid_attrs[$attr]) ) {
-        continue;
-      }
-
-      $value = $attr_node->value;
-
-      $target = $valid_attrs[$attr];
-      
-      // Look up $value in $target, if $target is an array:
-      if ( is_array($target) ) {
-        if ( isset($target[$value]) ) {
-          $style .= " " . self::_resolve_target($node, $target[$value], $value);
-        }
-      }
-      else {
-        // otherwise use target directly
-        $style .= " " . self::_resolve_target($node, $target, $value);
-      }
-    }
-    
-    if ( !is_null($style) ) {
-      $style = ltrim($style);
-      $node->setAttribute(self::$_style_attr, $style);
-    }
-    
-  }
-
-  /**
-   * @param DOMNode $node
-   * @param string  $target
-   * @param string      $value
-   *
-   * @return string
-   */
-  static protected function _resolve_target(DOMNode $node, $target, $value) {
-    if ( $target[0] === "!" ) {
-      // Function call
-      $func = "_" . mb_substr($target, 1);
-      return self::$func($node, $value);
-    }
-    
-    return $value ? sprintf($target, $value) : "";
-  }
-
-  /**
-   * @param DOMElement $node
-   * @param string     $new_style
-   */
-  static function append_style(DOMElement $node, $new_style) {
-    $style = rtrim($node->getAttribute(self::$_style_attr), ";");
-    $style .= $new_style;
-    $style = ltrim($style, ";");
-    $node->setAttribute(self::$_style_attr, $style);
-  }
-
-  /**
-   * @param DOMNode $node
-   *
-   * @return DOMNodeList|DOMElement[]
-   */
-  static protected function get_cell_list(DOMNode $node) {
-    $xpath = new DOMXpath($node->ownerDocument);
-    
-    switch($node->nodeName) {
-      default:
-      case "table":
-        $query = "tr/td | thead/tr/td | tbody/tr/td | tfoot/tr/td | tr/th | thead/tr/th | tbody/tr/th | tfoot/tr/th";
-        break;
-        
-      case "tbody":
-      case "tfoot":
-      case "thead":
-        $query = "tr/td | tr/th";
-        break;
-        
-      case "tr":
-        $query = "td | th";
-        break;
-    }
-    
-    return $xpath->query($query, $node);
-  }
-
-  /**
-   * @param string $value
-   *
-   * @return string
-   */
-  static protected function _get_valid_color($value) {
-    if ( preg_match('/^#?([0-9A-F]{6})$/i', $value, $matches) ) {
-      $value = "#$matches[1]";
-    }
-    
-    return $value;
-  }
-
-  /**
-   * @param DOMElement $node
-   * @param string     $value
-   *
-   * @return string
-   */
-  static protected function _set_color(DOMElement $node, $value) {
-    $value = self::_get_valid_color($value);
-    return "color: $value;";
-  }
-
-  /**
-   * @param DOMElement $node
-   * @param string     $value
-   *
-   * @return string
-   */
-  static protected function _set_background_color(DOMElement $node, $value) {
-    $value = self::_get_valid_color($value);
-    return "background-color: $value;";
-  }
-
-  /**
-   * @param DOMElement $node
-   * @param string     $value
-   *
-   * @return null
-   */
-  static protected function _set_table_cellpadding(DOMElement $node, $value) {
-    $cell_list = self::get_cell_list($node);
-    
-    foreach ($cell_list as $cell) {
-      self::append_style($cell, "; padding: {$value}px;");
-    }
-    
-    return null;
-  }
-
-  /**
-   * @param DOMElement $node
-   * @param string     $value
-   *
-   * @return string
-   */
-  static protected function _set_table_border(DOMElement $node, $value) {
-    $cell_list = self::get_cell_list($node);
-
-    foreach ($cell_list as $cell) {
-      $style = rtrim($cell->getAttribute(self::$_style_attr));
-      $style .= "; border-width: " . ($value > 0 ? 1 : 0) . "pt; border-style: inset;";
-      $style = ltrim($style, ";");
-      $cell->setAttribute(self::$_style_attr, $style);
-    }
-    
-    $style = rtrim($node->getAttribute(self::$_style_attr), ";");
-    $style .= "; border-width: $value" . "px; ";
-    return ltrim($style, "; ");
-  }
-
-  /**
-   * @param DOMElement $node
-   * @param string     $value
-   *
-   * @return string
-   */
-  static protected function _set_table_cellspacing(DOMElement $node, $value) {
-    $style = rtrim($node->getAttribute(self::$_style_attr), ";");
-
-    if ( $value == 0 ) {
-      $style .= "; border-collapse: collapse;";
-    }
-    else {
-      $style .= "; border-spacing: {$value}px; border-collapse: separate;";
-    }
-    
-    return ltrim($style, ";");
-  }
-
-  /**
-   * @param DOMElement $node
-   * @param string     $value
-   *
-   * @return null|string
-   */
-  static protected function _set_table_rules(DOMElement $node, $value) {
-    $new_style = "; border-collapse: collapse;";
-    
-    switch ($value) {
-    case "none":
-      $new_style .= "border-style: none;";
-      break;
-
-    case "groups":
-      // FIXME: unsupported
-      return null;
-
-    case "rows":
-      $new_style .= "border-style: solid none solid none; border-width: 1px; ";
-      break;
-
-    case "cols":
-      $new_style .= "border-style: none solid none solid; border-width: 1px; ";
-      break;
-
-    case "all":
-      $new_style .= "border-style: solid; border-width: 1px; ";
-      break;
-      
-    default:
-      // Invalid value
-      return null;
-    }
-
-    $cell_list = self::get_cell_list($node);
-    
-    foreach ($cell_list as $cell) {
-      $style = $cell->getAttribute(self::$_style_attr);
-      $style .= $new_style;
-      $cell->setAttribute(self::$_style_attr, $style);
-    }
-    
-    $style = rtrim($node->getAttribute(self::$_style_attr), ";");
-    $style .= "; border-collapse: collapse; ";
-    
-    return ltrim($style, "; ");
-  }
-
-  /**
-   * @param DOMElement $node
-   * @param string     $value
-   *
-   * @return string
-   */
-  static protected function _set_hr_size(DOMElement $node, $value) {
-    $style = rtrim($node->getAttribute(self::$_style_attr), ";");
-    $style .= "; border-width: ".max(0, $value-2)."; ";
-    return ltrim($style, "; ");
-  }
-
-  /**
-   * @param DOMElement $node
-   * @param string     $value
-   *
-   * @return null|string
-   */
-  static protected function _set_hr_align(DOMElement $node, $value) {
-    $style = rtrim($node->getAttribute(self::$_style_attr),";");
-    $width = $node->getAttribute("width");
-    
-    if ( $width == "" ) {
-      $width = "100%";
-    }
-
-    $remainder = 100 - (double)rtrim($width, "% ");
-    
-    switch ($value) {
-      case "left":
-        $style .= "; margin-right: $remainder %;";
-        break;
-  
-      case "right":
-        $style .= "; margin-left: $remainder %;";
-        break;
-  
-      case "center":
-        $style .= "; margin-left: auto; margin-right: auto;";
-        break;
-  
-      default:
-        return null;
-    }
-    
-    return ltrim($style, "; ");
-  }
-
-  /**
-   * @param DOMElement $node
-   * @param string     $value
-   *
-   * @return null
-   */
-  static protected function _set_table_row_align(DOMElement $node, $value) {
-    $cell_list = self::get_cell_list($node);
-
-    foreach ($cell_list as $cell) {
-      self::append_style($cell, "; text-align: $value;");
-    }
-
-    return null;
-  }
-
-  /**
-   * @param DOMElement $node
-   * @param string     $value
-   *
-   * @return null
-   */
-  static protected function _set_table_row_valign(DOMElement $node, $value) {
-    $cell_list = self::get_cell_list($node);
-
-    foreach ($cell_list as $cell) {
-      self::append_style($cell, "; vertical-align: $value;");
-    }
-
-    return null;
-  }
-
-  /**
-   * @param DOMElement $node
-   * @param string     $value
-   *
-   * @return null
-   */
-  static protected function _set_table_row_bgcolor(DOMElement $node, $value) {
-    $cell_list = self::get_cell_list($node);
-    $value = self::_get_valid_color($value);
-    
-    foreach ($cell_list as $cell) {
-      self::append_style($cell, "; background-color: $value;");
-    }
-
-    return null;
-  }
-
-  /**
-   * @param DOMElement $node
-   * @param string     $value
-   *
-   * @return null
-   */
-  static protected function _set_body_link(DOMElement $node, $value) {
-    $a_list = $node->getElementsByTagName("a");
-    $value = self::_get_valid_color($value);
-    
-    foreach ($a_list as $a) {
-      self::append_style($a, "; color: $value;");
-    }
-
-    return null;
-  }
-
-  /**
-   * @param DOMElement $node
-   * @param string     $value
-   *
-   * @return null
-   */
-  static protected function _set_basefont_size(DOMElement $node, $value) {
-    // FIXME: ? we don't actually set the font size of anything here, just
-    // the base size for later modification by  tags.
-    self::$_last_basefont_size = $value;
-    return null;
-  }
-
-  /**
-   * @param DOMElement $node
-   * @param string     $value
-   *
-   * @return string
-   */
-  static protected function _set_font_size(DOMElement $node, $value) {
-    $style = $node->getAttribute(self::$_style_attr);
-
-    if ( $value[0] === "-" || $value[0] === "+" ) {
-      $value = self::$_last_basefont_size + (int)$value;
-    }
-    
-    if ( isset(self::$_font_size_lookup[$value]) ) {
-      $style .= "; font-size: " . self::$_font_size_lookup[$value] . ";";
-    }
-    else {
-      $style .= "; font-size: $value;";
-    }
-    
-    return ltrim($style, "; ");
-  }
-}
diff --git a/library/vendor/dompdf/include/autoload.inc.php b/library/vendor/dompdf/include/autoload.inc.php
deleted file mode 100644
index 509d3e3bc..000000000
--- a/library/vendor/dompdf/include/autoload.inc.php
+++ /dev/null
@@ -1,86 +0,0 @@
-
- * @author  Fabien Ménager 
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
- 
-/**
- * DOMPDF autoload function
- *
- * If you have an existing autoload function, add a call to this function
- * from your existing __autoload() implementation.
- *
- * @param string $class
- */
-function DOMPDF_autoload($class) {
-  $filename = DOMPDF_INC_DIR . "/" . mb_strtolower($class) . ".cls.php";
-  
-  if ( is_file($filename) ) {
-    include_once $filename;
-  }
-}
-
-// If SPL autoload functions are available (PHP >= 5.1.2)
-if ( function_exists("spl_autoload_register") ) {
-  $autoload = "DOMPDF_autoload";
-  $funcs = spl_autoload_functions();
-  
-  // No functions currently in the stack. 
-  if ( !DOMPDF_AUTOLOAD_PREPEND || $funcs === false ) {
-    spl_autoload_register($autoload); 
-  }
-  
-  // If PHP >= 5.3 the $prepend argument is available
-  else if ( PHP_VERSION_ID >= 50300 ) {
-    spl_autoload_register($autoload, true, true); 
-  }
-  
-  else {
-    // Unregister existing autoloaders... 
-    $compat = (PHP_VERSION_ID <= 50102 && PHP_VERSION_ID >= 50100);
-              
-    foreach ($funcs as $func) { 
-      if (is_array($func)) { 
-        // :TRICKY: There are some compatibility issues and some 
-        // places where we need to error out 
-        $reflector = new ReflectionMethod($func[0], $func[1]); 
-        if (!$reflector->isStatic()) { 
-          throw new Exception('This function is not compatible with non-static object methods due to PHP Bug #44144.'); 
-        }
-        
-        // Suprisingly, spl_autoload_register supports the 
-        // Class::staticMethod callback format, although call_user_func doesn't 
-        if ($compat) $func = implode('::', $func); 
-      }
-      
-      spl_autoload_unregister($func); 
-    }
-    
-    // Register the new one, thus putting it at the front of the stack... 
-    spl_autoload_register($autoload); 
-    
-    // Now, go back and re-register all of our old ones. 
-    foreach ($funcs as $func) { 
-      spl_autoload_register($func); 
-    }
-    
-    // Be polite and ensure that userland autoload gets retained
-    if ( function_exists("__autoload") ) {
-      spl_autoload_register("__autoload");
-    }
-  }
-}
-
-else if ( !function_exists("__autoload") ) {
-  /**
-   * Default __autoload() function
-   *
-   * @param string $class
-   */
-  function __autoload($class) {
-    DOMPDF_autoload($class);
-  }
-}
diff --git a/library/vendor/dompdf/include/block_frame_decorator.cls.php b/library/vendor/dompdf/include/block_frame_decorator.cls.php
deleted file mode 100644
index 407635c0e..000000000
--- a/library/vendor/dompdf/include/block_frame_decorator.cls.php
+++ /dev/null
@@ -1,234 +0,0 @@
-
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-
-/**
- * Decorates frames for block layout
- *
- * @access private
- * @package dompdf
- */
-class Block_Frame_Decorator extends Frame_Decorator {
-  /**
-   * Current line index
-   *
-   * @var int
-   */
-  protected $_cl;
-  
-  /**
-   * The block's line boxes
-   * 
-   * @var Line_Box[]
-   */
-  protected $_line_boxes;
-
-  function __construct(Frame $frame, DOMPDF $dompdf) {
-    parent::__construct($frame, $dompdf);
-    
-    $this->_line_boxes = array(new Line_Box($this));
-    $this->_cl = 0;
-  }
-
-  function reset() {
-    parent::reset();
-    
-    $this->_line_boxes = array(new Line_Box($this));
-    $this->_cl = 0;
-  }
-
-  /**
-   * @return Line_Box
-   */
-  function get_current_line_box() {
-    return $this->_line_boxes[$this->_cl];
-  }
-
-  /**
-   * @return integer
-   */
-  function get_current_line_number() {
-    return $this->_cl;
-  }
-
-  /**
-   * @return Line_Box[]
-   */
-  function get_line_boxes() { 
-    return $this->_line_boxes; 
-  }
-
-  /**
-   * @param integer $i
-   */
-  function clear_line($i) {
-    if ( isset($this->_line_boxes[$i]) ) {
-      unset($this->_line_boxes[$i]);
-    }
-  }
-
-  /**
-   * @param Frame $frame
-   */
-  function add_frame_to_line(Frame $frame) {
-    if ( !$frame->is_in_flow() ) {
-      return;
-    }
-    
-    $style = $frame->get_style();
-    
-    $frame->set_containing_line($this->_line_boxes[$this->_cl]);
-    
-    /*
-    // Adds a new line after a block, only if certain conditions are met
-    if ((($frame instanceof Inline_Frame_Decorator && $frame->get_node()->nodeName !== "br") || 
-          $frame instanceof Text_Frame_Decorator && trim($frame->get_text())) && 
-        ($frame->get_prev_sibling() && $frame->get_prev_sibling()->get_style()->display === "block" && 
-         $this->_line_boxes[$this->_cl]->w > 0 )) {
-           
-           $this->maximize_line_height( $style->length_in_pt($style->line_height), $frame );
-           $this->add_line();
-         
-           // Add each child of the inline frame to the line individually
-           foreach ($frame->get_children() as $child)
-             $this->add_frame_to_line( $child );     
-    }
-    else*/
-
-    // Handle inline frames (which are effectively wrappers)
-    if ( $frame instanceof Inline_Frame_Decorator ) {
-
-      // Handle line breaks
-      if ( $frame->get_node()->nodeName === "br" ) {
-        $this->maximize_line_height( $style->length_in_pt($style->line_height), $frame );
-        $this->add_line(true);
-      }
-
-      return;
-    }
-
-    // Trim leading text if this is an empty line.  Kinda a hack to put it here,
-    // but what can you do...
-    if ( $this->get_current_line_box()->w == 0 &&
-         $frame->is_text_node() &&
-        !$frame->is_pre() ) {
-
-      $frame->set_text( ltrim($frame->get_text()) );
-      $frame->recalculate_width();
-    }
-
-    $w = $frame->get_margin_width();
-
-    if ( $w == 0 ) {
-      return;
-    }
-
-    // Debugging code:
-    /*
-    pre_r("\n

Adding frame to line:

"); - - // pre_r("Me: " . $this->get_node()->nodeName . " (" . spl_object_hash($this->get_node()) . ")"); - // pre_r("Node: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")"); - if ( $frame->is_text_node() ) - pre_r('"'.$frame->get_node()->nodeValue.'"'); - - pre_r("Line width: " . $this->_line_boxes[$this->_cl]->w); - pre_r("Frame: " . get_class($frame)); - pre_r("Frame width: " . $w); - pre_r("Frame height: " . $frame->get_margin_height()); - pre_r("Containing block width: " . $this->get_containing_block("w")); - */ - // End debugging - - $line = $this->_line_boxes[$this->_cl]; - if ( $line->left + $line->w + $line->right + $w > $this->get_containing_block("w")) { - $this->add_line(); - } - - $frame->position(); - - $current_line = $this->_line_boxes[$this->_cl]; - $current_line->add_frame($frame); - - if ( $frame->is_text_node() ) { - $current_line->wc += count(preg_split("/\s+/", trim($frame->get_text()))); - } - - $this->increase_line_width($w); - - $this->maximize_line_height($frame->get_margin_height(), $frame); - } - - function remove_frames_from_line(Frame $frame) { - // Search backwards through the lines for $frame - $i = $this->_cl; - $j = null; - - while ($i >= 0) { - if ( ($j = in_array($frame, $this->_line_boxes[$i]->get_frames(), true)) !== false ) { - break; - } - - $i--; - } - - if ( $j === false ) { - return; - } - - // Remove $frame and all frames that follow - while ($j < count($this->_line_boxes[$i]->get_frames())) { - $frames = $this->_line_boxes[$i]->get_frames(); - $f = $frames[$j]; - $frames[$j] = null; - unset($frames[$j]); - $j++; - $this->_line_boxes[$i]->w -= $f->get_margin_width(); - } - - // Recalculate the height of the line - $h = 0; - foreach ($this->_line_boxes[$i]->get_frames() as $f) { - $h = max( $h, $f->get_margin_height() ); - } - - $this->_line_boxes[$i]->h = $h; - - // Remove all lines that follow - while ($this->_cl > $i) { - $this->_line_boxes[ $this->_cl ] = null; - unset($this->_line_boxes[ $this->_cl ]); - $this->_cl--; - } - } - - function increase_line_width($w) { - $this->_line_boxes[ $this->_cl ]->w += $w; - } - - function maximize_line_height($val, Frame $frame) { - if ( $val > $this->_line_boxes[ $this->_cl ]->h ) { - $this->_line_boxes[ $this->_cl ]->tallest_frame = $frame; - $this->_line_boxes[ $this->_cl ]->h = $val; - } - } - - function add_line($br = false) { - -// if ( $this->_line_boxes[$this->_cl]["h"] == 0 ) //count($this->_line_boxes[$i]["frames"]) == 0 || -// return; - - $this->_line_boxes[$this->_cl]->br = $br; - $y = $this->_line_boxes[$this->_cl]->y + $this->_line_boxes[$this->_cl]->h; - - $new_line = new Line_Box($this, $y); - - $this->_line_boxes[ ++$this->_cl ] = $new_line; - } - - //........................................................................ -} diff --git a/library/vendor/dompdf/include/block_frame_reflower.cls.php b/library/vendor/dompdf/include/block_frame_reflower.cls.php deleted file mode 100644 index bbbdba96d..000000000 --- a/library/vendor/dompdf/include/block_frame_reflower.cls.php +++ /dev/null @@ -1,805 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Reflows block frames - * - * @access private - * @package dompdf - */ -class Block_Frame_Reflower extends Frame_Reflower { - // Minimum line width to justify, as fraction of available width - const MIN_JUSTIFY_WIDTH = 0.80; - - /** - * @var Block_Frame_Decorator - */ - protected $_frame; - - function __construct(Block_Frame_Decorator $frame) { parent::__construct($frame); } - - /** - * Calculate the ideal used value for the width property as per: - * http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins - * - * @param float $width - * @return array - */ - protected function _calculate_width($width) { - $frame = $this->_frame; - $style = $frame->get_style(); - $w = $frame->get_containing_block("w"); - - if ( $style->position === "fixed" ) { - $w = $frame->get_parent()->get_containing_block("w"); - } - - $rm = $style->length_in_pt($style->margin_right, $w); - $lm = $style->length_in_pt($style->margin_left, $w); - - $left = $style->length_in_pt($style->left, $w); - $right = $style->length_in_pt($style->right, $w); - - // Handle 'auto' values - $dims = array($style->border_left_width, - $style->border_right_width, - $style->padding_left, - $style->padding_right, - $width !== "auto" ? $width : 0, - $rm !== "auto" ? $rm : 0, - $lm !== "auto" ? $lm : 0); - - // absolutely positioned boxes take the 'left' and 'right' properties into account - if ( $frame->is_absolute() ) { - $absolute = true; - $dims[] = $left !== "auto" ? $left : 0; - $dims[] = $right !== "auto" ? $right : 0; - } - else { - $absolute = false; - } - - $sum = $style->length_in_pt($dims, $w); - - // Compare to the containing block - $diff = $w - $sum; - - if ( $diff > 0 ) { - - if ( $absolute ) { - - // resolve auto properties: see - // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width - - if ( $width === "auto" && $left === "auto" && $right === "auto" ) { - - if ( $lm === "auto" ) $lm = 0; - if ( $rm === "auto" ) $rm = 0; - - // Technically, the width should be "shrink-to-fit" i.e. based on the - // preferred width of the content... a little too costly here as a - // special case. Just get the width to take up the slack: - $left = 0; - $right = 0; - $width = $diff; - } - else if ( $width === "auto" ) { - - if ( $lm === "auto" ) $lm = 0; - if ( $rm === "auto" ) $rm = 0; - if ( $left === "auto" ) $left = 0; - if ( $right === "auto" ) $right = 0; - - $width = $diff; - } - else if ( $left === "auto" ) { - - if ( $lm === "auto" ) $lm = 0; - if ( $rm === "auto" ) $rm = 0; - if ( $right === "auto" ) $right = 0; - - $left = $diff; - } - else if ( $right === "auto" ) { - - if ( $lm === "auto" ) $lm = 0; - if ( $rm === "auto" ) $rm = 0; - - $right = $diff; - } - - } - else { - - // Find auto properties and get them to take up the slack - if ( $width === "auto" ) { - $width = $diff; - } - else if ( $lm === "auto" && $rm === "auto" ) { - $lm = $rm = round($diff / 2); - } - else if ( $lm === "auto" ) { - $lm = $diff; - } - else if ( $rm === "auto" ) { - $rm = $diff; - } - } - - } - else if ($diff < 0) { - - // We are over constrained--set margin-right to the difference - $rm = $diff; - - } - - return array( - "width" => $width, - "margin_left" => $lm, - "margin_right" => $rm, - "left" => $left, - "right" => $right, - ); - } - - /** - * Call the above function, but resolve max/min widths - * - * @throws DOMPDF_Exception - * @return array - */ - protected function _calculate_restricted_width() { - $frame = $this->_frame; - $style = $frame->get_style(); - $cb = $frame->get_containing_block(); - - if ( $style->position === "fixed" ) { - $cb = $frame->get_root()->get_containing_block(); - } - - //if ( $style->position === "absolute" ) - // $cb = $frame->find_positionned_parent()->get_containing_block(); - - if ( !isset($cb["w"]) ) { - throw new DOMPDF_Exception("Box property calculation requires containing block width"); - } - - // Treat width 100% as auto - if ( $style->width === "100%" ) { - $width = "auto"; - } - else { - $width = $style->length_in_pt($style->width, $cb["w"]); - } - - extract($this->_calculate_width($width)); - - // Handle min/max width - $min_width = $style->length_in_pt($style->min_width, $cb["w"]); - $max_width = $style->length_in_pt($style->max_width, $cb["w"]); - - if ( $max_width !== "none" && $min_width > $max_width ) { - list($max_width, $min_width) = array($min_width, $max_width); - } - - if ( $max_width !== "none" && $width > $max_width ) { - extract($this->_calculate_width($max_width)); - } - - if ( $width < $min_width ) { - extract($this->_calculate_width($min_width)); - } - - return array($width, $margin_left, $margin_right, $left, $right); - } - - /** - * Determine the unrestricted height of content within the block - * not by adding each line's height, but by getting the last line's position. - * This because lines could have been pushed lower by a clearing element. - * - * @return float - */ - protected function _calculate_content_height() { - $lines = $this->_frame->get_line_boxes(); - $height = 0; - - foreach ($lines as $line) { - $height += $line->h; - } - - /* - $first_line = reset($lines); - $last_line = end($lines); - $height2 = $last_line->y + $last_line->h - $first_line->y; - */ - - return $height; - } - - /** - * Determine the frame's restricted height - * - * @return array - */ - protected function _calculate_restricted_height() { - $frame = $this->_frame; - $style = $frame->get_style(); - $content_height = $this->_calculate_content_height(); - $cb = $frame->get_containing_block(); - - $height = $style->length_in_pt($style->height, $cb["h"]); - - $top = $style->length_in_pt($style->top, $cb["h"]); - $bottom = $style->length_in_pt($style->bottom, $cb["h"]); - - $margin_top = $style->length_in_pt($style->margin_top, $cb["h"]); - $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]); - - if ( $frame->is_absolute() ) { - - // see http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height - - $dims = array($top !== "auto" ? $top : 0, - $style->margin_top !== "auto" ? $style->margin_top : 0, - $style->padding_top, - $style->border_top_width, - $height !== "auto" ? $height : 0, - $style->border_bottom_width, - $style->padding_bottom, - $style->margin_bottom !== "auto" ? $style->margin_bottom : 0, - $bottom !== "auto" ? $bottom : 0); - - $sum = $style->length_in_pt($dims, $cb["h"]); - - $diff = $cb["h"] - $sum; - - if ( $diff > 0 ) { - - if ( $height === "auto" && $top === "auto" && $bottom === "auto" ) { - - if ( $margin_top === "auto" ) $margin_top = 0; - if ( $margin_bottom === "auto" ) $margin_bottom = 0; - - $height = $diff; - } - else if ( $height === "auto" && $top === "auto" ) { - - if ( $margin_top === "auto" ) $margin_top = 0; - if ( $margin_bottom === "auto" ) $margin_bottom = 0; - - $height = $content_height; - $top = $diff - $content_height; - } - else if ( $height === "auto" && $bottom === "auto" ) { - - if ( $margin_top === "auto" ) $margin_top = 0; - if ( $margin_bottom === "auto" ) $margin_bottom = 0; - - $height = $content_height; - $bottom = $diff - $content_height; - } - else if ( $top === "auto" && $bottom === "auto" ) { - - if ( $margin_top === "auto" ) $margin_top = 0; - if ( $margin_bottom === "auto" ) $margin_bottom = 0; - - $bottom = $diff; - } - else if ( $top === "auto" ) { - - if ( $margin_top === "auto" ) $margin_top = 0; - if ( $margin_bottom === "auto" ) $margin_bottom = 0; - - $top = $diff; - } - else if ( $height === "auto" ) { - - if ( $margin_top === "auto" ) $margin_top = 0; - if ( $margin_bottom === "auto" ) $margin_bottom = 0; - - $height = $diff; - } - else if ( $bottom === "auto" ) { - - if ( $margin_top === "auto" ) $margin_top = 0; - if ( $margin_bottom === "auto" ) $margin_bottom = 0; - - $bottom = $diff; - } - else { - - if ( $style->overflow === "visible" ) { - // set all autos to zero - if ( $margin_top === "auto" ) $margin_top = 0; - if ( $margin_bottom === "auto" ) $margin_bottom = 0; - if ( $top === "auto" ) $top = 0; - if ( $bottom === "auto" ) $bottom = 0; - if ( $height === "auto" ) $height = $content_height; - } - - // FIXME: overflow hidden - } - - } - - } - else { - - // Expand the height if overflow is visible - if ( $height === "auto" && $content_height > $height /* && $style->overflow === "visible" */) { - $height = $content_height; - } - - // FIXME: this should probably be moved to a seperate function as per - // _calculate_restricted_width - - // Only handle min/max height if the height is independent of the frame's content - if ( !($style->overflow === "visible" || - ($style->overflow === "hidden" && $height === "auto")) ) { - - $min_height = $style->min_height; - $max_height = $style->max_height; - - if ( isset($cb["h"]) ) { - $min_height = $style->length_in_pt($min_height, $cb["h"]); - $max_height = $style->length_in_pt($max_height, $cb["h"]); - - } - else if ( isset($cb["w"]) ) { - - if ( mb_strpos($min_height, "%") !== false ) { - $min_height = 0; - } - else { - $min_height = $style->length_in_pt($min_height, $cb["w"]); - } - - if ( mb_strpos($max_height, "%") !== false ) { - $max_height = "none"; - } - else { - $max_height = $style->length_in_pt($max_height, $cb["w"]); - } - } - - if ( $max_height !== "none" && $min_height > $max_height ) { - // Swap 'em - list($max_height, $min_height) = array($min_height, $max_height); - } - - if ( $max_height !== "none" && $height > $max_height ) { - $height = $max_height; - } - - if ( $height < $min_height ) { - $height = $min_height; - } - } - - } - - return array($height, $margin_top, $margin_bottom, $top, $bottom); - - } - - /** - * Adjust the justification of each of our lines. - * http://www.w3.org/TR/CSS21/text.html#propdef-text-align - */ - protected function _text_align() { - $style = $this->_frame->get_style(); - $w = $this->_frame->get_containing_block("w"); - $width = $style->length_in_pt($style->width, $w); - - switch ($style->text_align) { - default: - case "left": - foreach ($this->_frame->get_line_boxes() as $line) { - if ( !$line->left ) { - continue; - } - - foreach($line->get_frames() as $frame) { - if ( $frame instanceof Block_Frame_Decorator) { - continue; - } - $frame->set_position( $frame->get_position("x") + $line->left ); - } - } - return; - - case "right": - foreach ($this->_frame->get_line_boxes() as $line) { - // Move each child over by $dx - $dx = $width - $line->w - $line->right; - - foreach($line->get_frames() as $frame) { - // Block frames are not aligned by text-align - if ($frame instanceof Block_Frame_Decorator) { - continue; - } - - $frame->set_position( $frame->get_position("x") + $dx ); - } - } - break; - - - case "justify": - // We justify all lines except the last one - $lines = $this->_frame->get_line_boxes(); // needs to be a variable (strict standards) - array_pop($lines); - - foreach($lines as $i => $line) { - if ( $line->br ) { - unset($lines[$i]); - } - } - - // One space character's width. Will be used to get a more accurate spacing - $space_width = Font_Metrics::get_text_width(" ", $style->font_family, $style->font_size); - - foreach ($lines as $line) { - if ( $line->left ) { - foreach ( $line->get_frames() as $frame ) { - if ( !$frame instanceof Text_Frame_Decorator ) { - continue; - } - - $frame->set_position( $frame->get_position("x") + $line->left ); - } - } - - // Only set the spacing if the line is long enough. This is really - // just an aesthetic choice ;) - //if ( $line["left"] + $line["w"] + $line["right"] > self::MIN_JUSTIFY_WIDTH * $width ) { - - // Set the spacing for each child - if ( $line->wc > 1 ) { - $spacing = ($width - ($line->left + $line->w + $line->right) + $space_width) / ($line->wc - 1); - } - else { - $spacing = 0; - } - - $dx = 0; - foreach($line->get_frames() as $frame) { - if ( !$frame instanceof Text_Frame_Decorator ) { - continue; - } - - $text = $frame->get_text(); - $spaces = mb_substr_count($text, " "); - - $char_spacing = $style->length_in_pt($style->letter_spacing); - $_spacing = $spacing + $char_spacing; - - $frame->set_position( $frame->get_position("x") + $dx ); - $frame->set_text_spacing($_spacing); - - $dx += $spaces * $_spacing; - } - - // The line (should) now occupy the entire width - $line->w = $width; - - //} - } - break; - - case "center": - case "centre": - foreach ($this->_frame->get_line_boxes() as $line) { - // Centre each line by moving each frame in the line by: - $dx = ($width + $line->left - $line->w - $line->right ) / 2; - - foreach ($line->get_frames() as $frame) { - // Block frames are not aligned by text-align - if ($frame instanceof Block_Frame_Decorator) { - continue; - } - - $frame->set_position( $frame->get_position("x") + $dx ); - } - } - break; - } - } - - /** - * Align inline children vertically. - * Aligns each child vertically after each line is reflowed - */ - function vertical_align() { - - $canvas = null; - - foreach ( $this->_frame->get_line_boxes() as $line ) { - - $height = $line->h; - - foreach ( $line->get_frames() as $frame ) { - $style = $frame->get_style(); - - if ( $style->display !== "inline" ) { - continue; - } - - $align = $frame->get_parent()->get_style()->vertical_align; - - if ( !isset($canvas) ) { - $canvas = $frame->get_root()->get_dompdf()->get_canvas(); - } - - $baseline = $canvas->get_font_baseline($style->font_family, $style->font_size); - $y_offset = 0; - - switch ($align) { - case "baseline": - $y_offset = $height*0.8 - $baseline; // The 0.8 ratio is arbitrary until we find it's meaning - break; - - case "middle": - $y_offset = ($height*0.8 - $baseline) / 2; - break; - - case "sub": - $y_offset = 0.3 * $height; - break; - - case "super": - $y_offset = -0.2 * $height; - break; - - case "text-top": - case "top": // Not strictly accurate, but good enough for now - break; - - case "text-bottom": - case "bottom": - $y_offset = $height*0.8 - $baseline; - break; - } - - if ( $y_offset ) { - $frame->move(0, $y_offset); - } - } - } - } - - /** - * @param Frame $child - */ - function process_clear(Frame $child){ - $enable_css_float = $this->get_dompdf()->get_option("enable_css_float"); - if ( !$enable_css_float ) { - return; - } - - $child_style = $child->get_style(); - $root = $this->_frame->get_root(); - - // Handle "clear" - if ( $child_style->clear !== "none" ) { - $lowest_y = $root->get_lowest_float_offset($child); - - // If a float is still applying, we handle it - if ( $lowest_y ) { - if ( $child->is_in_flow() ) { - $line_box = $this->_frame->get_current_line_box(); - $line_box->y = $lowest_y + $child->get_margin_height(); - $line_box->left = 0; - $line_box->right = 0; - } - - $child->move(0, $lowest_y - $child->get_position("y")); - } - } - } - - /** - * @param Frame $child - * @param float $cb_x - * @param float $cb_w - */ - function process_float(Frame $child, $cb_x, $cb_w){ - $enable_css_float = $this->_frame->get_dompdf()->get_option("enable_css_float"); - if ( !$enable_css_float ) { - return; - } - - $child_style = $child->get_style(); - $root = $this->_frame->get_root(); - - // Handle "float" - if ( $child_style->float !== "none" ) { - $root->add_floating_frame($child); - - // Remove next frame's beginning whitespace - $next = $child->get_next_sibling(); - if ( $next && $next instanceof Text_Frame_Decorator) { - $next->set_text(ltrim($next->get_text())); - } - - $line_box = $this->_frame->get_current_line_box(); - list($old_x, $old_y) = $child->get_position(); - - $float_x = $cb_x; - $float_y = $old_y; - $float_w = $child->get_margin_width(); - - if ( $child_style->clear === "none" ) { - switch( $child_style->float ) { - case "left": - $float_x += $line_box->left; - break; - case "right": - $float_x += ($cb_w - $line_box->right - $float_w); - break; - } - } - else { - if ( $child_style->float === "right" ) { - $float_x += ($cb_w - $float_w); - } - } - - if ( $cb_w < $float_x + $float_w - $old_x ) { - // TODO handle when floating elements don't fit - } - - $line_box->get_float_offsets(); - - if ( $child->_float_next_line ) { - $float_y += $line_box->h; - } - - $child->set_position($float_x, $float_y); - $child->move($float_x - $old_x, $float_y - $old_y, true); - } - } - - /** - * @param Frame_Decorator $block - */ - function reflow(Block_Frame_Decorator $block = null) { - - // Check if a page break is forced - $page = $this->_frame->get_root(); - $page->check_forced_page_break($this->_frame); - - // Bail if the page is full - if ( $page->is_full() ) { - return; - } - - // Generated content - $this->_set_content(); - - // Collapse margins if required - $this->_collapse_margins(); - - $style = $this->_frame->get_style(); - $cb = $this->_frame->get_containing_block(); - - if ( $style->position === "fixed" ) { - $cb = $this->_frame->get_root()->get_containing_block(); - } - - // Determine the constraints imposed by this frame: calculate the width - // of the content area: - list($w, $left_margin, $right_margin, $left, $right) = $this->_calculate_restricted_width(); - - // Store the calculated properties - $style->width = $w . "pt"; - $style->margin_left = $left_margin."pt"; - $style->margin_right = $right_margin."pt"; - $style->left = $left ."pt"; - $style->right = $right . "pt"; - - // Update the position - $this->_frame->position(); - list($x, $y) = $this->_frame->get_position(); - - // Adjust the first line based on the text-indent property - $indent = $style->length_in_pt($style->text_indent, $cb["w"]); - $this->_frame->increase_line_width($indent); - - // Determine the content edge - $top = $style->length_in_pt(array($style->margin_top, - $style->padding_top, - $style->border_top_width), $cb["h"]); - - $bottom = $style->length_in_pt(array($style->border_bottom_width, - $style->margin_bottom, - $style->padding_bottom), $cb["h"]); - - $cb_x = $x + $left_margin + $style->length_in_pt(array($style->border_left_width, - $style->padding_left), $cb["w"]); - - $cb_y = $y + $top; - - $cb_h = ($cb["h"] + $cb["y"]) - $bottom - $cb_y; - - // Set the y position of the first line in this block - $line_box = $this->_frame->get_current_line_box(); - $line_box->y = $cb_y; - $line_box->get_float_offsets(); - - // Set the containing blocks and reflow each child - foreach ( $this->_frame->get_children() as $child ) { - - // Bail out if the page is full - if ( $page->is_full() ) { - break; - } - - $child->set_containing_block($cb_x, $cb_y, $w, $cb_h); - - $this->process_clear($child); - - $child->reflow($this->_frame); - - // Don't add the child to the line if a page break has occurred - if ( $page->check_page_break($child) ) { - break; - } - - $this->process_float($child, $cb_x, $w); - } - - // Determine our height - list($height, $margin_top, $margin_bottom, $top, $bottom) = $this->_calculate_restricted_height(); - $style->height = $height; - $style->margin_top = $margin_top; - $style->margin_bottom = $margin_bottom; - $style->top = $top; - $style->bottom = $bottom; - - $needs_reposition = ($style->position === "absolute" && ($style->right !== "auto" || $style->bottom !== "auto")); - - // Absolute positioning measurement - if ( $needs_reposition ) { - $orig_style = $this->_frame->get_original_style(); - if ( $orig_style->width === "auto" && ($orig_style->left === "auto" || $orig_style->right === "auto") ) { - $width = 0; - foreach ($this->_frame->get_line_boxes() as $line) { - $width = max($line->w, $width); - } - $style->width = $width; - } - - $style->left = $orig_style->left; - $style->right = $orig_style->right; - } - - $this->_text_align(); - $this->vertical_align(); - - // Absolute positioning - if ( $needs_reposition ) { - list($x, $y) = $this->_frame->get_position(); - $this->_frame->position(); - list($new_x, $new_y) = $this->_frame->get_position(); - $this->_frame->move($new_x-$x, $new_y-$y, true); - } - - if ( $block && $this->_frame->is_in_flow() ) { - $block->add_frame_to_line($this->_frame); - - // May be inline-block - if ( $style->display === "block" ) { - $block->add_line(); - } - } - } -} diff --git a/library/vendor/dompdf/include/block_positioner.cls.php b/library/vendor/dompdf/include/block_positioner.cls.php deleted file mode 100644 index d7e1c3fbc..000000000 --- a/library/vendor/dompdf/include/block_positioner.cls.php +++ /dev/null @@ -1,57 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Positions block frames - * - * @access private - * @package dompdf - */ -class Block_Positioner extends Positioner { - - - function __construct(Frame_Decorator $frame) { parent::__construct($frame); } - - //........................................................................ - - function position() { - $frame = $this->_frame; - $style = $frame->get_style(); - $cb = $frame->get_containing_block(); - $p = $frame->find_block_parent(); - - if ( $p ) { - $float = $style->float; - - $enable_css_float = $frame->get_dompdf()->get_option("enable_css_float"); - if ( !$enable_css_float || !$float || $float === "none" ) { - $p->add_line(true); - } - $y = $p->get_current_line_box()->y; - - } - else { - $y = $cb["y"]; - } - - $x = $cb["x"]; - - // Relative positionning - if ( $style->position === "relative" ) { - $top = $style->length_in_pt($style->top, $cb["h"]); - //$right = $style->length_in_pt($style->right, $cb["w"]); - //$bottom = $style->length_in_pt($style->bottom, $cb["h"]); - $left = $style->length_in_pt($style->left, $cb["w"]); - - $x += $left; - $y += $top; - } - - $frame->set_position($x, $y); - } -} diff --git a/library/vendor/dompdf/include/block_renderer.cls.php b/library/vendor/dompdf/include/block_renderer.cls.php deleted file mode 100644 index ef42c93d5..000000000 --- a/library/vendor/dompdf/include/block_renderer.cls.php +++ /dev/null @@ -1,230 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Renders block frames - * - * @access private - * @package dompdf - */ -class Block_Renderer extends Abstract_Renderer { - - //........................................................................ - - function render(Frame $frame) { - $style = $frame->get_style(); - $node = $frame->get_node(); - - list($x, $y, $w, $h) = $frame->get_border_box(); - - $this->_set_opacity( $frame->get_opacity( $style->opacity ) ); - - if ( $node->nodeName === "body" ) { - $h = $frame->get_containing_block("h") - $style->length_in_pt(array( - $style->margin_top, - $style->border_top_width, - $style->border_bottom_width, - $style->margin_bottom), - $style->width); - } - - // Handle anchors & links - if ( $node->nodeName === "a" && $href = $node->getAttribute("href") ) { - $this->_canvas->add_link($href, $x, $y, $w, $h); - } - - // Draw our background, border and content - list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h); - - if ( $tl + $tr + $br + $bl > 0 ) { - $this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl ); - } - - if ( ($bg = $style->background_color) !== "transparent" ) { - $this->_canvas->filled_rectangle( $x, $y, $w, $h, $bg ); - } - - if ( ($url = $style->background_image) && $url !== "none" ) { - $this->_background_image($url, $x, $y, $w, $h, $style); - } - - if ( $tl + $tr + $br + $bl > 0 ) { - $this->_canvas->clipping_end(); - } - - $border_box = array($x, $y, $w, $h); - $this->_render_border($frame, $border_box); - $this->_render_outline($frame, $border_box); - - if (DEBUG_LAYOUT && DEBUG_LAYOUT_BLOCKS) { - $this->_debug_layout($frame->get_border_box(), "red"); - if (DEBUG_LAYOUT_PADDINGBOX) { - $this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5)); - } - } - - if (DEBUG_LAYOUT && DEBUG_LAYOUT_LINES && $frame->get_decorator()) { - foreach ($frame->get_decorator()->get_line_boxes() as $line) { - $frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange"); - } - } - } - - protected function _render_border(Frame_Decorator $frame, $border_box = null, $corner_style = "bevel") { - $style = $frame->get_style(); - $bp = $style->get_border_properties(); - - if ( empty($border_box) ) { - $border_box = $frame->get_border_box(); - } - - // find the radius - $radius = $style->get_computed_border_radius($border_box[2], $border_box[3]); // w, h - - // Short-cut: If all the borders are "solid" with the same color and style, and no radius, we'd better draw a rectangle - if ( - in_array($bp["top"]["style"], array("solid", "dashed", "dotted")) && - $bp["top"] == $bp["right"] && - $bp["right"] == $bp["bottom"] && - $bp["bottom"] == $bp["left"] && - array_sum($radius) == 0 - ) { - $props = $bp["top"]; - if ( $props["color"] === "transparent" || $props["width"] <= 0 ) return; - - list($x, $y, $w, $h) = $border_box; - $width = $style->length_in_pt($props["width"]); - $pattern = $this->_get_dash_pattern($props["style"], $width); - $this->_canvas->rectangle($x + $width / 2, $y + $width / 2, $w - $width, $h - $width, $props["color"], $width, $pattern); - return; - } - - // Do it the long way - $widths = array($style->length_in_pt($bp["top"]["width"]), - $style->length_in_pt($bp["right"]["width"]), - $style->length_in_pt($bp["bottom"]["width"]), - $style->length_in_pt($bp["left"]["width"])); - - foreach ($bp as $side => $props) { - list($x, $y, $w, $h) = $border_box; - $length = 0; - $r1 = 0; - $r2 = 0; - - if ( !$props["style"] || - $props["style"] === "none" || - $props["width"] <= 0 || - $props["color"] == "transparent" ) - continue; - - switch($side) { - case "top": - $length = $w; - $r1 = $radius["top-left"]; - $r2 = $radius["top-right"]; - break; - - case "bottom": - $length = $w; - $y += $h; - $r1 = $radius["bottom-left"]; - $r2 = $radius["bottom-right"]; - break; - - case "left": - $length = $h; - $r1 = $radius["top-left"]; - $r2 = $radius["bottom-left"]; - break; - - case "right": - $length = $h; - $x += $w; - $r1 = $radius["top-right"]; - $r2 = $radius["bottom-right"]; - break; - default: - break; - } - $method = "_border_" . $props["style"]; - - // draw rounded corners - $this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style, $r1, $r2); - } - } - - protected function _render_outline(Frame_Decorator $frame, $border_box = null, $corner_style = "bevel") { - $style = $frame->get_style(); - - $props = array( - "width" => $style->outline_width, - "style" => $style->outline_style, - "color" => $style->outline_color, - ); - - if ( !$props["style"] || $props["style"] === "none" || $props["width"] <= 0 ) - return; - - if ( empty($border_box) ) { - $border_box = $frame->get_border_box(); - } - - $offset = $style->length_in_pt($props["width"]); - $pattern = $this->_get_dash_pattern($props["style"], $offset); - - // If the outline style is "solid" we'd better draw a rectangle - if ( in_array($props["style"], array("solid", "dashed", "dotted")) ) { - $border_box[0] -= $offset / 2; - $border_box[1] -= $offset / 2; - $border_box[2] += $offset; - $border_box[3] += $offset; - - list($x, $y, $w, $h) = $border_box; - $this->_canvas->rectangle($x, $y, $w, $h, $props["color"], $offset, $pattern); - return; - } - - $border_box[0] -= $offset; - $border_box[1] -= $offset; - $border_box[2] += $offset * 2; - $border_box[3] += $offset * 2; - - $method = "_border_" . $props["style"]; - $widths = array_fill(0, 4, $props["width"]); - $sides = array("top", "right", "left", "bottom"); - $length = 0; - - foreach ($sides as $side) { - list($x, $y, $w, $h) = $border_box; - - switch($side) { - case "top": - $length = $w; - break; - - case "bottom": - $length = $w; - $y += $h; - break; - - case "left": - $length = $h; - break; - - case "right": - $length = $h; - $x += $w; - break; - default: - break; - } - - $this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style); - } - } -} diff --git a/library/vendor/dompdf/include/cached_pdf_decorator.cls.php b/library/vendor/dompdf/include/cached_pdf_decorator.cls.php deleted file mode 100644 index 519e572e8..000000000 --- a/library/vendor/dompdf/include/cached_pdf_decorator.cls.php +++ /dev/null @@ -1,164 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Caching canvas implementation - * - * Each rendered page is serialized and stored in the {@link Page_Cache}. - * This is useful for static forms/pages that do not need to be re-rendered - * all the time. - * - * This class decorates normal CPDF_Adapters. It is currently completely - * experimental. - * - * @access private - * @package dompdf - */ -class Cached_PDF_Decorator extends CPDF_Adapter implements Canvas { - /** - * @var CPDF_Adapter - */ - protected $_pdf; - protected $_cache_id; - protected $_current_page_id; - protected $_fonts; // fonts used in this document - - function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) { - $this->_fonts = array(); - } - - /** - * Must be called after constructor - * - * @param int $cache_id - * @param CPDF_Adapter $pdf - */ - function init($cache_id, CPDF_Adapter $pdf) { - $this->_cache_id = $cache_id; - $this->_pdf = $pdf; - $this->_current_page_id = $this->_pdf->open_object(); - } - - //........................................................................ - - function get_cpdf() { return $this->_pdf->get_cpdf(); } - - function open_object() { $this->_pdf->open_object(); } - function reopen_object($object) { $this->_pdf->reopen_object($object); } - - function close_object() { $this->_pdf->close_object(); } - - function add_object($object, $where = 'all') { $this->_pdf->add_object($object, $where); } - - function serialize_object($id) { $this->_pdf->serialize_object($id); } - - function reopen_serialized_object($obj) { $this->_pdf->reopen_serialized_object($obj); } - - //........................................................................ - - function get_width() { return $this->_pdf->get_width(); } - function get_height() { return $this->_pdf->get_height(); } - function get_page_number() { return $this->_pdf->get_page_number(); } - function get_page_count() { return $this->_pdf->get_page_count(); } - - function set_page_number($num) { $this->_pdf->set_page_number($num); } - function set_page_count($count) { $this->_pdf->set_page_count($count); } - - function line($x1, $y1, $x2, $y2, $color, $width, $style = array()) { - $this->_pdf->line($x1, $y1, $x2, $y2, $color, $width, $style); - } - - function rectangle($x1, $y1, $w, $h, $color, $width, $style = array()) { - $this->_pdf->rectangle($x1, $y1, $w, $h, $color, $width, $style); - } - - function filled_rectangle($x1, $y1, $w, $h, $color) { - $this->_pdf->filled_rectangle($x1, $y1, $w, $h, $color); - } - - function polygon($points, $color, $width = null, $style = array(), $fill = false) { - $this->_pdf->polygon($points, $color, $width, $style, $fill); - } - - function circle($x, $y, $r1, $color, $width = null, $style = null, $fill = false) { - $this->_pdf->circle($x, $y, $r1, $color, $width, $style, $fill); - } - - function image($img_url, $x, $y, $w, $h, $resolution = "normal") { - $this->_pdf->image($img_url, $x, $y, $w, $h, $resolution); - } - - function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) { - $this->_fonts[$font] = true; - $this->_pdf->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle); - } - - function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) { - - // We want to remove this from cached pages since it may not be correct - $this->_pdf->close_object(); - $this->_pdf->page_text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle); - $this->_pdf->reopen_object($this->_current_page_id); - } - - function page_script($script, $type = 'text/php') { - - // We want to remove this from cached pages since it may not be correct - $this->_pdf->close_object(); - $this->_pdf->page_script($script, $type); - $this->_pdf->reopen_object($this->_current_page_id); - } - - function new_page() { - $this->_pdf->close_object(); - - // Add the object to the current page - $this->_pdf->add_object($this->_current_page_id, "add"); - $this->_pdf->new_page(); - - Page_Cache::store_page($this->_cache_id, - $this->_pdf->get_page_number() - 1, - $this->_pdf->serialize_object($this->_current_page_id)); - - $this->_current_page_id = $this->_pdf->open_object(); - return $this->_current_page_id; - } - - function stream($filename, $options = null) { - // Store the last page in the page cache - if ( !is_null($this->_current_page_id) ) { - $this->_pdf->close_object(); - $this->_pdf->add_object($this->_current_page_id, "add"); - Page_Cache::store_page($this->_cache_id, - $this->_pdf->get_page_number(), - $this->_pdf->serialize_object($this->_current_page_id)); - Page_Cache::store_fonts($this->_cache_id, $this->_fonts); - $this->_current_page_id = null; - } - - $this->_pdf->stream($filename); - - } - - function output($options = null) { - // Store the last page in the page cache - if ( !is_null($this->_current_page_id) ) { - $this->_pdf->close_object(); - $this->_pdf->add_object($this->_current_page_id, "add"); - Page_Cache::store_page($this->_cache_id, - $this->_pdf->get_page_number(), - $this->_pdf->serialize_object($this->_current_page_id)); - $this->_current_page_id = null; - } - - return $this->_pdf->output(); - } - - function get_messages() { return $this->_pdf->get_messages(); } - -} diff --git a/library/vendor/dompdf/include/canvas.cls.php b/library/vendor/dompdf/include/canvas.cls.php deleted file mode 100644 index 0158df6b1..000000000 --- a/library/vendor/dompdf/include/canvas.cls.php +++ /dev/null @@ -1,385 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Main rendering interface - * - * Currently {@link CPDF_Adapter}, {@link PDFLib_Adapter}, {@link TCPDF_Adapter}, and {@link GD_Adapter} - * implement this interface. - * - * Implementations should measure x and y increasing to the left and down, - * respectively, with the origin in the top left corner. Implementations - * are free to use a unit other than points for length, but I can't - * guarantee that the results will look any good. - * - * @package dompdf - */ -interface Canvas { - function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf); - - /** - * @return DOMPDF - */ - function get_dompdf(); - - /** - * Returns the current page number - * - * @return int - */ - function get_page_number(); - - /** - * Returns the total number of pages - * - * @return int - */ - function get_page_count(); - - /** - * Sets the total number of pages - * - * @param int $count - */ - function set_page_count($count); - - /** - * Draws a line from x1,y1 to x2,y2 - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the format of the - * $style parameter (aka dash). - * - * @param float $x1 - * @param float $y1 - * @param float $x2 - * @param float $y2 - * @param array $color - * @param float $width - * @param array $style - */ - function line($x1, $y1, $x2, $y2, $color, $width, $style = null); - - /** - * Draws a rectangle at x1,y1 with width w and height h - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the $style - * parameter (aka dash) - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - * @param array $color - * @param float $width - * @param array $style - */ - function rectangle($x1, $y1, $w, $h, $color, $width, $style = null); - - /** - * Draws a filled rectangle at x1,y1 with width w and height h - * - * See {@link Style::munge_color()} for the format of the color array. - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - * @param array $color - */ - function filled_rectangle($x1, $y1, $w, $h, $color); - - /** - * Starts a clipping rectangle at x1,y1 with width w and height h - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - */ - function clipping_rectangle($x1, $y1, $w, $h); - - /** - * Starts a rounded clipping rectangle at x1,y1 with width w and height h - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - * @param float $tl - * @param float $tr - * @param float $br - * @param float $bl - * - * @return - */ - function clipping_roundrectangle($x1, $y1, $w, $h, $tl, $tr, $br, $bl); - - /** - * Ends the last clipping shape - */ - function clipping_end(); - - /** - * Save current state - */ - function save(); - - /** - * Restore last state - */ - function restore(); - - /** - * Rotate - */ - function rotate($angle, $x, $y); - - /** - * Skew - */ - function skew($angle_x, $angle_y, $x, $y); - - /** - * Scale - */ - function scale($s_x, $s_y, $x, $y); - - /** - * Translate - */ - function translate($t_x, $t_y); - - /** - * Transform - */ - function transform($a, $b, $c, $d, $e, $f); - - /** - * Draws a polygon - * - * The polygon is formed by joining all the points stored in the $points - * array. $points has the following structure: - * - * array(0 => x1, - * 1 => y1, - * 2 => x2, - * 3 => y2, - * ... - * ); - * - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the $style - * parameter (aka dash) - * - * @param array $points - * @param array $color - * @param float $width - * @param array $style - * @param bool $fill Fills the polygon if true - */ - function polygon($points, $color, $width = null, $style = null, $fill = false); - - /** - * Draws a circle at $x,$y with radius $r - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the $style - * parameter (aka dash) - * - * @param float $x - * @param float $y - * @param float $r - * @param array $color - * @param float $width - * @param array $style - * @param bool $fill Fills the circle if true - */ - function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false); - - /** - * Add an image to the pdf. - * - * The image is placed at the specified x and y coordinates with the - * given width and height. - * - * @param string $img_url the path to the image - * @param float $x x position - * @param float $y y position - * @param int $w width (in pixels) - * @param int $h height (in pixels) - * @param string $resolution The resolution of the image - */ - function image($img_url, $x, $y, $w, $h, $resolution = "normal"); - - /** - * Add an arc to the PDF - * See {@link Style::munge_color()} for the format of the color array. - * - * @param float $x X coordinate of the arc - * @param float $y Y coordinate of the arc - * @param float $r1 Radius 1 - * @param float $r2 Radius 2 - * @param float $astart Start angle in degrees - * @param float $aend End angle in degrees - * @param array $color Color - * @param float $width - * @param array $style - * - * @return void - */ - function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array()); - - /** - * Writes text at the specified x and y coordinates - * See {@link Style::munge_color()} for the format of the color array. - * - * @param float $x - * @param float $y - * @param string $text the text to write - * @param string $font the font file to use - * @param float $size the font size, in points - * @param array $color - * @param float $word_space word spacing adjustment - * @param float $char_space char spacing adjustment - * @param float $angle angle - * - * @return void - */ - function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0); - - /** - * Add a named destination (similar to ... in html) - * - * @param string $anchorname The name of the named destination - */ - function add_named_dest($anchorname); - - /** - * Add a link to the pdf - * - * @param string $url The url to link to - * @param float $x The x position of the link - * @param float $y The y position of the link - * @param float $width The width of the link - * @param float $height The height of the link - * - * @return void - */ - function add_link($url, $x, $y, $width, $height); - - /** - * Add meta information to the pdf - * - * @param string $name Label of the value (Creator, Producer, etc.) - * @param string $value The text to set - */ - function add_info($name, $value); - - /** - * Calculates text size, in points - * - * @param string $text the text to be sized - * @param string $font the desired font - * @param float $size the desired font size - * @param float $word_spacing word spacing, if any - * @param float $char_spacing - * - * @return float - */ - function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0); - - /** - * Calculates font height, in points - * - * @param string $font - * @param float $size - * - * @return float - */ - function get_font_height($font, $size); - - /** - * Calculates font baseline, in points - * - * @param string $font - * @param float $size - * - * @return float - */ - function get_font_baseline($font, $size); - - /** - * Returns the font x-height, in points - * - * @param string $font - * @param float $size - * - * @return float - */ - //function get_font_x_height($font, $size); - - /** - * Sets the opacity - * - * @param float $opacity - * @param string $mode - */ - function set_opacity($opacity, $mode = "Normal"); - - /** - * Sets the default view - * - * @param string $view - * 'XYZ' left, top, zoom - * 'Fit' - * 'FitH' top - * 'FitV' left - * 'FitR' left,bottom,right - * 'FitB' - * 'FitBH' top - * 'FitBV' left - * @param array $options - * - * @return void - */ - function set_default_view($view, $options = array()); - - /** - * @param string $script - * - * @return void - */ - function javascript($script); - - /** - * Starts a new page - * - * Subsequent drawing operations will appear on the new page. - */ - function new_page(); - - /** - * Streams the PDF directly to the browser - * - * @param string $filename the name of the PDF file - * @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0 - */ - function stream($filename, $options = null); - - /** - * Returns the PDF as a string - * - * @param array $options associative array: 'compress' => 1 or 0 - * @return string - */ - function output($options = null); -} diff --git a/library/vendor/dompdf/include/canvas_factory.cls.php b/library/vendor/dompdf/include/canvas_factory.cls.php deleted file mode 100644 index ef634e6b0..000000000 --- a/library/vendor/dompdf/include/canvas_factory.cls.php +++ /dev/null @@ -1,63 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Create canvas instances - * - * The canvas factory creates canvas instances based on the - * availability of rendering backends and config options. - * - * @package dompdf - */ -class Canvas_Factory { - - /** - * Constructor is private: this is a static class - */ - private function __construct() { } - - /** - * @param DOMPDF $dompdf - * @param string|array $paper - * @param string $orientation - * @param string $class - * - * @return Canvas - */ - static function get_instance(DOMPDF $dompdf, $paper = null, $orientation = null, $class = null) { - - $backend = strtolower(DOMPDF_PDF_BACKEND); - - if ( isset($class) && class_exists($class, false) ) { - $class .= "_Adapter"; - } - - else if ( (DOMPDF_PDF_BACKEND === "auto" || $backend === "pdflib" ) && - class_exists("PDFLib", false) ) { - $class = "PDFLib_Adapter"; - } - - // FIXME The TCPDF adapter is not ready yet - //else if ( (DOMPDF_PDF_BACKEND === "auto" || $backend === "cpdf") ) - // $class = "CPDF_Adapter"; - - else if ( $backend === "tcpdf" ) { - $class = "TCPDF_Adapter"; - } - - else if ( $backend === "gd" ) { - $class = "GD_Adapter"; - } - - else { - $class = "CPDF_Adapter"; - } - - return new $class($paper, $orientation, $dompdf); - } -} diff --git a/library/vendor/dompdf/include/cellmap.cls.php b/library/vendor/dompdf/include/cellmap.cls.php deleted file mode 100644 index 0982849b3..000000000 --- a/library/vendor/dompdf/include/cellmap.cls.php +++ /dev/null @@ -1,790 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Maps table cells to the table grid. - * - * This class resolves borders in tables with collapsed borders and helps - * place row & column spanned table cells. - * - * @access private - * @package dompdf - */ -class Cellmap { - - /** - * Border style weight lookup for collapsed border resolution. - * - * @var array - */ - static protected $_BORDER_STYLE_SCORE = array( - "inset" => 1, - "groove" => 2, - "outset" => 3, - "ridge" => 4, - "dotted" => 5, - "dashed" => 6, - "solid" => 7, - "double" => 8, - "hidden" => 9, - "none" => 0, - ); - - /** - * The table object this cellmap is attached to. - * - * @var Table_Frame_Decorator - */ - protected $_table; - - /** - * The total number of rows in the table - * - * @var int - */ - protected $_num_rows; - - /** - * The total number of columns in the table - * - * @var int - */ - protected $_num_cols; - - /** - * 2D array mapping to frames - * - * @var Frame[][] - */ - protected $_cells; - - /** - * 1D array of column dimensions - * - * @var array - */ - protected $_columns; - - /** - * 1D array of row dimensions - * - * @var array - */ - protected $_rows; - - /** - * 2D array of border specs - * - * @var array - */ - protected $_borders; - - /** - * 1D Array mapping frames to (multiple) pairs, keyed on frame_id. - * - * @var Frame[] - */ - protected $_frames; - - /** - * Current column when adding cells, 0-based - * - * @var int - */ - private $__col; - - /** - * Current row when adding cells, 0-based - * - * @var int - */ - private $__row; - - /** - * Tells wether the columns' width can be modified - * - * @var bool - */ - private $_columns_locked = false; - - /** - * Tells wether the table has table-layout:fixed - * - * @var bool - */ - private $_fixed_layout = false; - - //........................................................................ - - function __construct(Table_Frame_Decorator $table) { - $this->_table = $table; - $this->reset(); - } - - function __destruct() { - clear_object($this); - } - //........................................................................ - - function reset() { - $this->_num_rows = 0; - $this->_num_cols = 0; - - $this->_cells = array(); - $this->_frames = array(); - - if ( !$this->_columns_locked ) { - $this->_columns = array(); - } - - $this->_rows = array(); - - $this->_borders = array(); - - $this->__col = $this->__row = 0; - } - - //........................................................................ - - function lock_columns() { - $this->_columns_locked = true; - } - - function is_columns_locked() { - return $this->_columns_locked; - } - - function set_layout_fixed($fixed) { - $this->_fixed_layout = $fixed; - } - - function is_layout_fixed() { - return $this->_fixed_layout; - } - - function get_num_rows() { return $this->_num_rows; } - function get_num_cols() { return $this->_num_cols; } - - function &get_columns() { - return $this->_columns; - } - - function set_columns($columns) { - $this->_columns = $columns; - } - - function &get_column($i) { - if ( !isset($this->_columns[$i]) ) { - $this->_columns[$i] = array( - "x" => 0, - "min-width" => 0, - "max-width" => 0, - "used-width" => null, - "absolute" => 0, - "percent" => 0, - "auto" => true, - ); - } - - return $this->_columns[$i]; - } - - function &get_rows() { - return $this->_rows; - } - - function &get_row($j) { - if ( !isset($this->_rows[$j]) ) { - $this->_rows[$j] = array( - "y" => 0, - "first-column" => 0, - "height" => null, - ); - } - - return $this->_rows[$j]; - } - - function get_border($i, $j, $h_v, $prop = null) { - if ( !isset($this->_borders[$i][$j][$h_v]) ) { - $this->_borders[$i][$j][$h_v] = array( - "width" => 0, - "style" => "solid", - "color" => "black", - ); - } - - if ( isset($prop) ) { - return $this->_borders[$i][$j][$h_v][$prop]; - } - - return $this->_borders[$i][$j][$h_v]; - } - - function get_border_properties($i, $j) { - return array( - "top" => $this->get_border($i, $j, "horizontal"), - "right" => $this->get_border($i, $j+1, "vertical"), - "bottom" => $this->get_border($i+1, $j, "horizontal"), - "left" => $this->get_border($i, $j, "vertical"), - ); - } - - //........................................................................ - - function get_spanned_cells(Frame $frame) { - $key = $frame->get_id(); - - if ( !isset($this->_frames[$key]) ) { - throw new DOMPDF_Exception("Frame not found in cellmap"); - } - - return $this->_frames[$key]; - - } - - function frame_exists_in_cellmap(Frame $frame) { - $key = $frame->get_id(); - return isset($this->_frames[$key]); - } - - function get_frame_position(Frame $frame) { - global $_dompdf_warnings; - - $key = $frame->get_id(); - - if ( !isset($this->_frames[$key]) ) { - throw new DOMPDF_Exception("Frame not found in cellmap"); - } - - $col = $this->_frames[$key]["columns"][0]; - $row = $this->_frames[$key]["rows"][0]; - - if ( !isset($this->_columns[$col])) { - $_dompdf_warnings[] = "Frame not found in columns array. Check your table layout for missing or extra TDs."; - $x = 0; - } - else { - $x = $this->_columns[$col]["x"]; - } - - if ( !isset($this->_rows[$row])) { - $_dompdf_warnings[] = "Frame not found in row array. Check your table layout for missing or extra TDs."; - $y = 0; - } - else { - $y = $this->_rows[$row]["y"]; - } - - return array($x, $y, "x" => $x, "y" => $y); - } - - function get_frame_width(Frame $frame) { - $key = $frame->get_id(); - - if ( !isset($this->_frames[$key]) ) { - throw new DOMPDF_Exception("Frame not found in cellmap"); - } - - $cols = $this->_frames[$key]["columns"]; - $w = 0; - foreach ($cols as $i) { - $w += $this->_columns[$i]["used-width"]; - } - - return $w; - } - - function get_frame_height(Frame $frame) { - $key = $frame->get_id(); - - if ( !isset($this->_frames[$key]) ) { - throw new DOMPDF_Exception("Frame not found in cellmap"); - } - - $rows = $this->_frames[$key]["rows"]; - $h = 0; - foreach ($rows as $i) { - if ( !isset($this->_rows[$i]) ) { - throw new Exception("The row #$i could not be found, please file an issue in the tracker with the HTML code"); - } - - $h += $this->_rows[$i]["height"]; - } - - return $h; - } - - - //........................................................................ - - function set_column_width($j, $width) { - if ( $this->_columns_locked ) { - return; - } - - $col =& $this->get_column($j); - $col["used-width"] = $width; - $next_col =& $this->get_column($j+1); - $next_col["x"] = $next_col["x"] + $width; - } - - function set_row_height($i, $height) { - $row =& $this->get_row($i); - - if ( $row["height"] !== null && $height <= $row["height"] ) { - return; - } - - $row["height"] = $height; - $next_row =& $this->get_row($i+1); - $next_row["y"] = $row["y"] + $height; - - } - - //........................................................................ - - - protected function _resolve_border($i, $j, $h_v, $border_spec) { - $n_width = $border_spec["width"]; - $n_style = $border_spec["style"]; - - if ( !isset($this->_borders[$i][$j][$h_v]) ) { - $this->_borders[$i][$j][$h_v] = $border_spec; - return $this->_borders[$i][$j][$h_v]["width"]; - } - - $border = &$this->_borders[$i][$j][$h_v]; - - $o_width = $border["width"]; - $o_style = $border["style"]; - - if ( ($n_style === "hidden" || - $n_width > $o_width || - $o_style === "none") - - or - - ($o_width == $n_width && - in_array($n_style, self::$_BORDER_STYLE_SCORE) && - self::$_BORDER_STYLE_SCORE[ $n_style ] > self::$_BORDER_STYLE_SCORE[ $o_style ]) ) { - $border = $border_spec; - } - - return $border["width"]; - } - - //........................................................................ - - function add_frame(Frame $frame) { - - $style = $frame->get_style(); - $display = $style->display; - - $collapse = $this->_table->get_style()->border_collapse == "collapse"; - - // Recursively add the frames within tables, table-row-groups and table-rows - if ( $display === "table-row" || - $display === "table" || - $display === "inline-table" || - in_array($display, Table_Frame_Decorator::$ROW_GROUPS) ) { - - $start_row = $this->__row; - foreach ( $frame->get_children() as $child ) { - $this->add_frame( $child ); - } - - if ( $display === "table-row" ) { - $this->add_row(); - } - - $num_rows = $this->__row - $start_row - 1; - $key = $frame->get_id(); - - // Row groups always span across the entire table - $this->_frames[$key]["columns"] = range(0,max(0,$this->_num_cols-1)); - $this->_frames[$key]["rows"] = range($start_row, max(0, $this->__row - 1)); - $this->_frames[$key]["frame"] = $frame; - - if ( $display !== "table-row" && $collapse ) { - - $bp = $style->get_border_properties(); - - // Resolve the borders - for ( $i = 0; $i < $num_rows+1; $i++) { - $this->_resolve_border($start_row + $i, 0, "vertical", $bp["left"]); - $this->_resolve_border($start_row + $i, $this->_num_cols, "vertical", $bp["right"]); - } - - for ( $j = 0; $j < $this->_num_cols; $j++) { - $this->_resolve_border($start_row, $j, "horizontal", $bp["top"]); - $this->_resolve_border($this->__row, $j, "horizontal", $bp["bottom"]); - } - } - - - return; - } - - $node = $frame->get_node(); - - // Determine where this cell is going - $colspan = $node->getAttribute("colspan"); - $rowspan = $node->getAttribute("rowspan"); - - if ( !$colspan ) { - $colspan = 1; - $node->setAttribute("colspan",1); - } - - if ( !$rowspan ) { - $rowspan = 1; - $node->setAttribute("rowspan",1); - } - $key = $frame->get_id(); - - $bp = $style->get_border_properties(); - - - // Add the frame to the cellmap - $max_left = $max_right = 0; - - // Find the next available column (fix by Ciro Mondueri) - $ac = $this->__col; - while ( isset($this->_cells[$this->__row][$ac]) ) { - $ac++; - } - - $this->__col = $ac; - - // Rows: - for ( $i = 0; $i < $rowspan; $i++ ) { - $row = $this->__row + $i; - - $this->_frames[$key]["rows"][] = $row; - - for ( $j = 0; $j < $colspan; $j++) { - $this->_cells[$row][$this->__col + $j] = $frame; - } - - if ( $collapse ) { - // Resolve vertical borders - $max_left = max($max_left, $this->_resolve_border($row, $this->__col, "vertical", $bp["left"])); - $max_right = max($max_right, $this->_resolve_border($row, $this->__col + $colspan, "vertical", $bp["right"])); - } - } - - $max_top = $max_bottom = 0; - - // Columns: - for ( $j = 0; $j < $colspan; $j++ ) { - $col = $this->__col + $j; - $this->_frames[$key]["columns"][] = $col; - - if ( $collapse ) { - // Resolve horizontal borders - $max_top = max($max_top, $this->_resolve_border($this->__row, $col, "horizontal", $bp["top"])); - $max_bottom = max($max_bottom, $this->_resolve_border($this->__row + $rowspan, $col, "horizontal", $bp["bottom"])); - } - } - - $this->_frames[$key]["frame"] = $frame; - - // Handle seperated border model - if ( !$collapse ) { - list($h, $v) = $this->_table->get_style()->border_spacing; - - // Border spacing is effectively a margin between cells - $v = $style->length_in_pt($v) / 2; - $h = $style->length_in_pt($h) / 2; - $style->margin = "$v $h"; - - // The additional 1/2 width gets added to the table proper - } - else { - // Drop the frame's actual border - $style->border_left_width = $max_left / 2; - $style->border_right_width = $max_right / 2; - $style->border_top_width = $max_top / 2; - $style->border_bottom_width = $max_bottom / 2; - $style->margin = "none"; - } - - if ( !$this->_columns_locked ) { - // Resolve the frame's width - if ( $this->_fixed_layout ) { - list($frame_min, $frame_max) = array(0, 10e-10); - } - else { - list($frame_min, $frame_max) = $frame->get_min_max_width(); - } - - $width = $style->width; - - $val = null; - if ( is_percent($width) ) { - $var = "percent"; - $val = (float)rtrim($width, "% ") / $colspan; - } - else if ( $width !== "auto" ) { - $var = "absolute"; - $val = $style->length_in_pt($frame_min) / $colspan; - } - - $min = 0; - $max = 0; - for ( $cs = 0; $cs < $colspan; $cs++ ) { - - // Resolve the frame's width(s) with other cells - $col =& $this->get_column( $this->__col + $cs ); - - // Note: $var is either 'percent' or 'absolute'. We compare the - // requested percentage or absolute values with the existing widths - // and adjust accordingly. - if ( isset($var) && $val > $col[$var] ) { - $col[$var] = $val; - $col["auto"] = false; - } - - $min += $col["min-width"]; - $max += $col["max-width"]; - } - - if ( $frame_min > $min ) { - // The frame needs more space. Expand each sub-column - // FIXME try to avoid putting this dummy value when table-layout:fixed - $inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_min - $min) / $colspan); - for ($c = 0; $c < $colspan; $c++) { - $col =& $this->get_column($this->__col + $c); - $col["min-width"] += $inc; - } - } - - if ( $frame_max > $max ) { - // FIXME try to avoid putting this dummy value when table-layout:fixed - $inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_max - $max) / $colspan); - for ($c = 0; $c < $colspan; $c++) { - $col =& $this->get_column($this->__col + $c); - $col["max-width"] += $inc; - } - } - } - - $this->__col += $colspan; - if ( $this->__col > $this->_num_cols ) - $this->_num_cols = $this->__col; - - } - - //........................................................................ - - function add_row() { - - $this->__row++; - $this->_num_rows++; - - // Find the next available column - $i = 0; - while ( isset($this->_cells[$this->__row][$i]) ) { - $i++; - } - - $this->__col = $i; - - } - - //........................................................................ - - /** - * Remove a row from the cellmap. - * - * @param Frame - */ - function remove_row(Frame $row) { - - $key = $row->get_id(); - if ( !isset($this->_frames[$key]) ) { - return; // Presumably this row has alredy been removed - } - - $this->_row = $this->_num_rows--; - - $rows = $this->_frames[$key]["rows"]; - $columns = $this->_frames[$key]["columns"]; - - // Remove all frames from this row - foreach ( $rows as $r ) { - foreach ( $columns as $c ) { - if ( isset($this->_cells[$r][$c]) ) { - $id = $this->_cells[$r][$c]->get_id(); - - $this->_frames[$id] = null; - unset($this->_frames[$id]); - - $this->_cells[$r][$c] = null; - unset($this->_cells[$r][$c]); - } - } - - $this->_rows[$r] = null; - unset($this->_rows[$r]); - } - - $this->_frames[$key] = null; - unset($this->_frames[$key]); - - } - - /** - * Remove a row group from the cellmap. - * - * @param Frame $group The group to remove - */ - function remove_row_group(Frame $group) { - - $key = $group->get_id(); - if ( !isset($this->_frames[$key]) ) { - return; // Presumably this row has alredy been removed - } - - $iter = $group->get_first_child(); - while ($iter) { - $this->remove_row($iter); - $iter = $iter->get_next_sibling(); - } - - $this->_frames[$key] = null; - unset($this->_frames[$key]); - } - - /** - * Update a row group after rows have been removed - * - * @param Frame $group The group to update - * @param Frame $last_row The last row in the row group - */ - function update_row_group(Frame $group, Frame $last_row) { - - $g_key = $group->get_id(); - $r_key = $last_row->get_id(); - - $r_rows = $this->_frames[$r_key]["rows"]; - $this->_frames[$g_key]["rows"] = range( $this->_frames[$g_key]["rows"][0], end($r_rows) ); - - } - - //........................................................................ - - function assign_x_positions() { - // Pre-condition: widths must be resolved and assigned to columns and - // column[0]["x"] must be set. - - if ( $this->_columns_locked ) { - return; - } - - $x = $this->_columns[0]["x"]; - foreach ( array_keys($this->_columns) as $j ) { - $this->_columns[$j]["x"] = $x; - $x += $this->_columns[$j]["used-width"]; - } - - } - - function assign_frame_heights() { - // Pre-condition: widths and heights of each column & row must be - // calcluated - - foreach ( $this->_frames as $arr ) { - $frame = $arr["frame"]; - - $h = 0; - foreach( $arr["rows"] as $row ) { - if ( !isset($this->_rows[$row]) ) { - // The row has been removed because of a page split, so skip it. - continue; - } - - $h += $this->_rows[$row]["height"]; - } - - if ( $frame instanceof Table_Cell_Frame_Decorator ) { - $frame->set_cell_height($h); - } - else { - $frame->get_style()->height = $h; - } - } - - } - - //........................................................................ - - /** - * Re-adjust frame height if the table height is larger than its content - */ - function set_frame_heights($table_height, $content_height) { - - - // Distribute the increased height proportionally amongst each row - foreach ( $this->_frames as $arr ) { - $frame = $arr["frame"]; - - $h = 0; - foreach ($arr["rows"] as $row ) { - if ( !isset($this->_rows[$row]) ) { - continue; - } - - $h += $this->_rows[$row]["height"]; - } - - if ( $content_height > 0 ) { - $new_height = ($h / $content_height) * $table_height; - } - else { - $new_height = 0; - } - - if ( $frame instanceof Table_Cell_Frame_Decorator ) { - $frame->set_cell_height($new_height); - } - else { - $frame->get_style()->height = $new_height; - } - } - - } - - //........................................................................ - - // Used for debugging: - function __toString() { - $str = ""; - $str .= "Columns:
"; - $str .= pre_r($this->_columns, true); - $str .= "Rows:
"; - $str .= pre_r($this->_rows, true); - - $str .= "Frames:
"; - $arr = array(); - foreach ( $this->_frames as $key => $val ) { - $arr[$key] = array("columns" => $val["columns"], "rows" => $val["rows"]); - } - - $str .= pre_r($arr, true); - - if ( php_sapi_name() == "cli" ) { - $str = strip_tags(str_replace(array("
","",""), - array("\n",chr(27)."[01;33m", chr(27)."[0m"), - $str)); - } - - return $str; - } -} diff --git a/library/vendor/dompdf/include/cpdf_adapter.cls.php b/library/vendor/dompdf/include/cpdf_adapter.cls.php deleted file mode 100644 index 041e1f375..000000000 --- a/library/vendor/dompdf/include/cpdf_adapter.cls.php +++ /dev/null @@ -1,877 +0,0 @@ - - * @author Orion Richardson - * @author Helmut Tischer - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -// FIXME: Need to sanity check inputs to this class -require_once(DOMPDF_LIB_DIR . "/class.pdf.php"); - -/** - * PDF rendering interface - * - * CPDF_Adapter provides a simple stateless interface to the stateful one - * provided by the Cpdf class. - * - * Unless otherwise mentioned, all dimensions are in points (1/72 in). The - * coordinate origin is in the top left corner, and y values increase - * downwards. - * - * See {@link http://www.ros.co.nz/pdf/} for more complete documentation - * on the underlying {@link Cpdf} class. - * - * @package dompdf - */ -class CPDF_Adapter implements Canvas { - - /** - * Dimensions of paper sizes in points - * - * @var array; - */ - static $PAPER_SIZES = array( - "4a0" => array(0,0,4767.87,6740.79), - "2a0" => array(0,0,3370.39,4767.87), - "a0" => array(0,0,2383.94,3370.39), - "a1" => array(0,0,1683.78,2383.94), - "a2" => array(0,0,1190.55,1683.78), - "a3" => array(0,0,841.89,1190.55), - "a4" => array(0,0,595.28,841.89), - "a5" => array(0,0,419.53,595.28), - "a6" => array(0,0,297.64,419.53), - "a7" => array(0,0,209.76,297.64), - "a8" => array(0,0,147.40,209.76), - "a9" => array(0,0,104.88,147.40), - "a10" => array(0,0,73.70,104.88), - "b0" => array(0,0,2834.65,4008.19), - "b1" => array(0,0,2004.09,2834.65), - "b2" => array(0,0,1417.32,2004.09), - "b3" => array(0,0,1000.63,1417.32), - "b4" => array(0,0,708.66,1000.63), - "b5" => array(0,0,498.90,708.66), - "b6" => array(0,0,354.33,498.90), - "b7" => array(0,0,249.45,354.33), - "b8" => array(0,0,175.75,249.45), - "b9" => array(0,0,124.72,175.75), - "b10" => array(0,0,87.87,124.72), - "c0" => array(0,0,2599.37,3676.54), - "c1" => array(0,0,1836.85,2599.37), - "c2" => array(0,0,1298.27,1836.85), - "c3" => array(0,0,918.43,1298.27), - "c4" => array(0,0,649.13,918.43), - "c5" => array(0,0,459.21,649.13), - "c6" => array(0,0,323.15,459.21), - "c7" => array(0,0,229.61,323.15), - "c8" => array(0,0,161.57,229.61), - "c9" => array(0,0,113.39,161.57), - "c10" => array(0,0,79.37,113.39), - "ra0" => array(0,0,2437.80,3458.27), - "ra1" => array(0,0,1729.13,2437.80), - "ra2" => array(0,0,1218.90,1729.13), - "ra3" => array(0,0,864.57,1218.90), - "ra4" => array(0,0,609.45,864.57), - "sra0" => array(0,0,2551.18,3628.35), - "sra1" => array(0,0,1814.17,2551.18), - "sra2" => array(0,0,1275.59,1814.17), - "sra3" => array(0,0,907.09,1275.59), - "sra4" => array(0,0,637.80,907.09), - "letter" => array(0,0,612.00,792.00), - "legal" => array(0,0,612.00,1008.00), - "ledger" => array(0,0,1224.00, 792.00), - "tabloid" => array(0,0,792.00, 1224.00), - "executive" => array(0,0,521.86,756.00), - "folio" => array(0,0,612.00,936.00), - "commercial #10 envelope" => array(0,0,684,297), - "catalog #10 1/2 envelope" => array(0,0,648,864), - "8.5x11" => array(0,0,612.00,792.00), - "8.5x14" => array(0,0,612.00,1008.0), - "11x17" => array(0,0,792.00, 1224.00), - ); - - /** - * The DOMPDF object - * - * @var DOMPDF - */ - private $_dompdf; - - /** - * Instance of Cpdf class - * - * @var Cpdf - */ - private $_pdf; - - /** - * PDF width, in points - * - * @var float - */ - private $_width; - - /** - * PDF height, in points - * - * @var float; - */ - private $_height; - - /** - * Current page number - * - * @var int - */ - private $_page_number; - - /** - * Total number of pages - * - * @var int - */ - private $_page_count; - - /** - * Text to display on every page - * - * @var array - */ - private $_page_text; - - /** - * Array of pages for accesing after rendering is initially complete - * - * @var array - */ - private $_pages; - - /** - * Array of temporary cached images to be deleted when processing is complete - * - * @var array - */ - private $_image_cache; - - /** - * Class constructor - * - * @param mixed $paper The size of paper to use in this PDF ({@link CPDF_Adapter::$PAPER_SIZES}) - * @param string $orientation The orientation of the document (either 'landscape' or 'portrait') - * @param DOMPDF $dompdf The DOMPDF instance - */ - function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) { - if ( is_array($paper) ) { - $size = $paper; - } - else if ( isset(self::$PAPER_SIZES[mb_strtolower($paper)]) ) { - $size = self::$PAPER_SIZES[mb_strtolower($paper)]; - } - else { - $size = self::$PAPER_SIZES["letter"]; - } - - if ( mb_strtolower($orientation) === "landscape" ) { - list($size[2], $size[3]) = array($size[3], $size[2]); - } - - $this->_dompdf = $dompdf; - - $this->_pdf = new Cpdf( - $size, - $dompdf->get_option("enable_unicode"), - $dompdf->get_option("font_cache"), - $dompdf->get_option("temp_dir") - ); - - $this->_pdf->addInfo("Creator", "DOMPDF"); - $time = substr_replace(date('YmdHisO'), '\'', -2, 0).'\''; - $this->_pdf->addInfo("CreationDate", "D:$time"); - $this->_pdf->addInfo("ModDate", "D:$time"); - - $this->_width = $size[2] - $size[0]; - $this->_height= $size[3] - $size[1]; - - $this->_page_number = $this->_page_count = 1; - $this->_page_text = array(); - - $this->_pages = array($this->_pdf->getFirstPageId()); - - $this->_image_cache = array(); - } - - function get_dompdf(){ - return $this->_dompdf; - } - - /** - * Class destructor - * - * Deletes all temporary image files - */ - function __destruct() { - foreach ($this->_image_cache as $img) { - // The file might be already deleted by 3rd party tmp cleaner, - // the file might not have been created at all - // (if image outputting commands failed) - // or because the destructor was called twice accidentally. - if (!file_exists($img)) { - continue; - } - - if (DEBUGPNG) print '[__destruct unlink '.$img.']'; - if (!DEBUGKEEPTEMP) unlink($img); - } - } - - /** - * Returns the Cpdf instance - * - * @return Cpdf - */ - function get_cpdf() { - return $this->_pdf; - } - - /** - * Add meta information to the PDF - * - * @param string $label label of the value (Creator, Producer, etc.) - * @param string $value the text to set - */ - function add_info($label, $value) { - $this->_pdf->addInfo($label, $value); - } - - /** - * Opens a new 'object' - * - * While an object is open, all drawing actions are recored in the object, - * as opposed to being drawn on the current page. Objects can be added - * later to a specific page or to several pages. - * - * The return value is an integer ID for the new object. - * - * @see CPDF_Adapter::close_object() - * @see CPDF_Adapter::add_object() - * - * @return int - */ - function open_object() { - $ret = $this->_pdf->openObject(); - $this->_pdf->saveState(); - return $ret; - } - - /** - * Reopens an existing 'object' - * - * @see CPDF_Adapter::open_object() - * @param int $object the ID of a previously opened object - */ - function reopen_object($object) { - $this->_pdf->reopenObject($object); - $this->_pdf->saveState(); - } - - /** - * Closes the current 'object' - * - * @see CPDF_Adapter::open_object() - */ - function close_object() { - $this->_pdf->restoreState(); - $this->_pdf->closeObject(); - } - - /** - * Adds a specified 'object' to the document - * - * $object int specifying an object created with {@link - * CPDF_Adapter::open_object()}. $where can be one of: - * - 'add' add to current page only - * - 'all' add to every page from the current one onwards - * - 'odd' add to all odd numbered pages from now on - * - 'even' add to all even numbered pages from now on - * - 'next' add the object to the next page only - * - 'nextodd' add to all odd numbered pages from the next one - * - 'nexteven' add to all even numbered pages from the next one - * - * @see Cpdf::addObject() - * - * @param int $object - * @param string $where - */ - function add_object($object, $where = 'all') { - $this->_pdf->addObject($object, $where); - } - - /** - * Stops the specified 'object' from appearing in the document. - * - * The object will stop being displayed on the page following the current - * one. - * - * @param int $object - */ - function stop_object($object) { - $this->_pdf->stopObject($object); - } - - /** - * @access private - */ - function serialize_object($id) { - // Serialize the pdf object's current state for retrieval later - return $this->_pdf->serializeObject($id); - } - - /** - * @access private - */ - function reopen_serialized_object($obj) { - return $this->_pdf->restoreSerializedObject($obj); - } - - //........................................................................ - - /** - * Returns the PDF's width in points - * @return float - */ - function get_width() { return $this->_width; } - - /** - * Returns the PDF's height in points - * @return float - */ - function get_height() { return $this->_height; } - - /** - * Returns the current page number - * @return int - */ - function get_page_number() { return $this->_page_number; } - - /** - * Returns the total number of pages in the document - * @return int - */ - function get_page_count() { return $this->_page_count; } - - /** - * Sets the current page number - * - * @param int $num - */ - function set_page_number($num) { $this->_page_number = $num; } - - /** - * Sets the page count - * - * @param int $count - */ - function set_page_count($count) { $this->_page_count = $count; } - - /** - * Sets the stroke color - * - * See {@link Style::set_color()} for the format of the color array. - * @param array $color - */ - protected function _set_stroke_color($color) { - $this->_pdf->setStrokeColor($color); - } - - /** - * Sets the fill colour - * - * See {@link Style::set_color()} for the format of the colour array. - * @param array $color - */ - protected function _set_fill_color($color) { - $this->_pdf->setColor($color); - } - - /** - * Sets line transparency - * @see Cpdf::setLineTransparency() - * - * Valid blend modes are (case-sensitive): - * - * Normal, Multiply, Screen, Overlay, Darken, Lighten, - * ColorDodge, ColorBurn, HardLight, SoftLight, Difference, - * Exclusion - * - * @param string $mode the blending mode to use - * @param float $opacity 0.0 fully transparent, 1.0 fully opaque - */ - protected function _set_line_transparency($mode, $opacity) { - $this->_pdf->setLineTransparency($mode, $opacity); - } - - /** - * Sets fill transparency - * @see Cpdf::setFillTransparency() - * - * Valid blend modes are (case-sensitive): - * - * Normal, Multiply, Screen, Overlay, Darken, Lighten, - * ColorDogde, ColorBurn, HardLight, SoftLight, Difference, - * Exclusion - * - * @param string $mode the blending mode to use - * @param float $opacity 0.0 fully transparent, 1.0 fully opaque - */ - protected function _set_fill_transparency($mode, $opacity) { - $this->_pdf->setFillTransparency($mode, $opacity); - } - - /** - * Sets the line style - * - * @see Cpdf::setLineStyle() - * - * @param float $width - * @param string $cap - * @param string $join - * @param array $dash - */ - protected function _set_line_style($width, $cap, $join, $dash) { - $this->_pdf->setLineStyle($width, $cap, $join, $dash); - } - - /** - * Sets the opacity - * - * @param $opacity - * @param $mode - */ - function set_opacity($opacity, $mode = "Normal") { - $this->_set_line_transparency($mode, $opacity); - $this->_set_fill_transparency($mode, $opacity); - } - - function set_default_view($view, $options = array()) { - array_unshift($options, $view); - call_user_func_array(array($this->_pdf, "openHere"), $options); - } - - /** - * Remaps y coords from 4th to 1st quadrant - * - * @param float $y - * @return float - */ - protected function y($y) { - return $this->_height - $y; - } - - // Canvas implementation - function line($x1, $y1, $x2, $y2, $color, $width, $style = array()) { - $this->_set_stroke_color($color); - $this->_set_line_style($width, "butt", "", $style); - - $this->_pdf->line($x1, $this->y($y1), - $x2, $this->y($y2)); - } - - function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array()) { - $this->_set_stroke_color($color); - $this->_set_line_style($width, "butt", "", $style); - - $this->_pdf->ellipse($x, $this->y($y), $r1, $r2, 0, 8, $astart, $aend, false, false, true, false); - } - - //........................................................................ - - /** - * Convert a GIF or BMP image to a PNG image - * - * @param string $image_url - * @param integer $type - * - * @throws DOMPDF_Exception - * @return string The url of the newly converted image - */ - protected function _convert_gif_bmp_to_png($image_url, $type) { - $image_type = Image_Cache::type_to_ext($type); - $func_name = "imagecreatefrom$image_type"; - - if ( !function_exists($func_name) ) { - throw new DOMPDF_Exception("Function $func_name() not found. Cannot convert $image_type image: $image_url. Please install the image PHP extension."); - } - - set_error_handler("record_warnings"); - $im = $func_name($image_url); - - if ( $im ) { - imageinterlace($im, false); - - $tmp_dir = $this->_dompdf->get_option("temp_dir"); - $tmp_name = tempnam($tmp_dir, "{$image_type}dompdf_img_"); - @unlink($tmp_name); - $filename = "$tmp_name.png"; - $this->_image_cache[] = $filename; - - imagepng($im, $filename); - imagedestroy($im); - } - else { - $filename = Image_Cache::$broken_image; - } - - restore_error_handler(); - - return $filename; - } - - function rectangle($x1, $y1, $w, $h, $color, $width, $style = array()) { - $this->_set_stroke_color($color); - $this->_set_line_style($width, "butt", "", $style); - $this->_pdf->rectangle($x1, $this->y($y1) - $h, $w, $h); - } - - function filled_rectangle($x1, $y1, $w, $h, $color) { - $this->_set_fill_color($color); - $this->_pdf->filledRectangle($x1, $this->y($y1) - $h, $w, $h); - } - - function clipping_rectangle($x1, $y1, $w, $h) { - $this->_pdf->clippingRectangle($x1, $this->y($y1) - $h, $w, $h); - } - - function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) { - $this->_pdf->clippingRectangleRounded($x1, $this->y($y1) - $h, $w, $h, $rTL, $rTR, $rBR, $rBL); - } - - function clipping_end() { - $this->_pdf->clippingEnd(); - } - - function save() { - $this->_pdf->saveState(); - } - - function restore() { - $this->_pdf->restoreState(); - } - - function rotate($angle, $x, $y) { - $this->_pdf->rotate($angle, $x, $y); - } - - function skew($angle_x, $angle_y, $x, $y) { - $this->_pdf->skew($angle_x, $angle_y, $x, $y); - } - - function scale($s_x, $s_y, $x, $y) { - $this->_pdf->scale($s_x, $s_y, $x, $y); - } - - function translate($t_x, $t_y) { - $this->_pdf->translate($t_x, $t_y); - } - - function transform($a, $b, $c, $d, $e, $f) { - $this->_pdf->transform(array($a, $b, $c, $d, $e, $f)); - } - - function polygon($points, $color, $width = null, $style = array(), $fill = false) { - $this->_set_fill_color($color); - $this->_set_stroke_color($color); - - // Adjust y values - for ( $i = 1; $i < count($points); $i += 2) { - $points[$i] = $this->y($points[$i]); - } - - $this->_pdf->polygon($points, count($points) / 2, $fill); - } - - function circle($x, $y, $r1, $color, $width = null, $style = null, $fill = false) { - $this->_set_fill_color($color); - $this->_set_stroke_color($color); - - if ( !$fill && isset($width) ) { - $this->_set_line_style($width, "round", "round", $style); - } - - $this->_pdf->ellipse($x, $this->y($y), $r1, 0, 0, 8, 0, 360, 1, $fill); - } - - function image($img, $x, $y, $w, $h, $resolution = "normal") { - list($width, $height, $type) = dompdf_getimagesize($img, $this->_dompdf->get_http_context()); - - $debug_png = $this->_dompdf->get_option("debug_png"); - - if ($debug_png) print "[image:$img|$width|$height|$type]"; - - switch ($type) { - case IMAGETYPE_JPEG: - if ($debug_png) print '!!!jpg!!!'; - $this->_pdf->addJpegFromFile($img, $x, $this->y($y) - $h, $w, $h); - break; - - case IMAGETYPE_GIF: - case IMAGETYPE_BMP: - if ($debug_png) print '!!!bmp or gif!!!'; - // @todo use cache for BMP and GIF - $img = $this->_convert_gif_bmp_to_png($img, $type); - - case IMAGETYPE_PNG: - if ($debug_png) print '!!!png!!!'; - - $this->_pdf->addPngFromFile($img, $x, $this->y($y) - $h, $w, $h); - break; - - default: - if ($debug_png) print '!!!unknown!!!'; - } - } - - function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) { - $pdf = $this->_pdf; - - $pdf->setColor($color); - - $font .= ".afm"; - $pdf->selectFont($font); - - //Font_Metrics::get_font_height($font, $size) == - //$this->get_font_height($font, $size) == - //$this->_pdf->selectFont($font),$this->_pdf->getFontHeight($size) - //- FontBBoxheight+FontHeightOffset, scaled to $size, in pt - //$this->_pdf->getFontDescender($size) - //- Descender scaled to size - // - //$this->_pdf->fonts[$this->_pdf->currentFont] sizes: - //['FontBBox'][0] left, ['FontBBox'][1] bottom, ['FontBBox'][2] right, ['FontBBox'][3] top - //Maximum extent of all glyphs of the font from the baseline point - //['Ascender'] maximum height above baseline except accents - //['Descender'] maximum depth below baseline, negative number means below baseline - //['FontHeightOffset'] manual enhancement of .afm files to trim windows fonts. currently not used. - //Values are in 1/1000 pt for a font size of 1 pt - // - //['FontBBox'][1] should be close to ['Descender'] - //['FontBBox'][3] should be close to ['Ascender']+Accents - //in practice, FontBBox values are a little bigger - // - //The text position is referenced to the baseline, not to the lower corner of the FontBBox, - //for what the left,top corner is given. - //FontBBox spans also the background box for the text. - //If the lower corner would be used as reference point, the Descents of the glyphs would - //hang over the background box border. - //Therefore compensate only the extent above the Baseline. - // - //print '
['.$font.','.$size.','.$pdf->getFontHeight($size).','.$pdf->getFontDescender($size).','.$pdf->fonts[$pdf->currentFont]['FontBBox'][3].','.$pdf->fonts[$pdf->currentFont]['FontBBox'][1].','.$pdf->fonts[$pdf->currentFont]['FontHeightOffset'].','.$pdf->fonts[$pdf->currentFont]['Ascender'].','.$pdf->fonts[$pdf->currentFont]['Descender'].']
'; - // - //$pdf->addText($x, $this->y($y) - ($pdf->fonts[$pdf->currentFont]['FontBBox'][3]*$size)/1000, $size, $text, $angle, $word_space, $char_space); - $pdf->addText($x, $this->y($y) - $pdf->getFontHeight($size), $size, $text, $angle, $word_space, $char_space); - } - - //........................................................................ - - function javascript($code) { - $this->_pdf->addJavascript($code); - } - - //........................................................................ - - /** - * Add a named destination (similar to ... in html) - * - * @param string $anchorname The name of the named destination - */ - function add_named_dest($anchorname) { - $this->_pdf->addDestination($anchorname, "Fit"); - } - - //........................................................................ - - /** - * Add a link to the pdf - * - * @param string $url The url to link to - * @param float $x The x position of the link - * @param float $y The y position of the link - * @param float $width The width of the link - * @param float $height The height of the link - */ - function add_link($url, $x, $y, $width, $height) { - - $y = $this->y($y) - $height; - - if ( strpos($url, '#') === 0 ) { - // Local link - $name = substr($url,1); - if ( $name ) { - $this->_pdf->addInternalLink($name, $x, $y, $x + $width, $y + $height); - } - - } - else { - $this->_pdf->addLink(rawurldecode($url), $x, $y, $x + $width, $y + $height); - } - } - - function get_text_width($text, $font, $size, $word_spacing = 0, $char_spacing = 0) { - $this->_pdf->selectFont($font); - - $unicode = $this->_dompdf->get_option("enable_unicode"); - if (!$unicode) { - $text = mb_convert_encoding($text, 'Windows-1252', 'UTF-8'); - } - - return $this->_pdf->getTextWidth($size, $text, $word_spacing, $char_spacing); - } - - function register_string_subset($font, $string) { - $this->_pdf->registerText($font, $string); - } - - function get_font_height($font, $size) { - $this->_pdf->selectFont($font); - - $ratio = $this->_dompdf->get_option("font_height_ratio"); - return $this->_pdf->getFontHeight($size) * $ratio; - } - - /*function get_font_x_height($font, $size) { - $this->_pdf->selectFont($font); - $ratio = $this->_dompdf->get_option("font_height_ratio"); - return $this->_pdf->getFontXHeight($size) * $ratio; - }*/ - - function get_font_baseline($font, $size) { - $ratio = $this->_dompdf->get_option("font_height_ratio"); - return $this->get_font_height($font, $size) / $ratio; - } - - /** - * Writes text at the specified x and y coordinates on every page - * - * The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced - * with their current values. - * - * See {@link Style::munge_color()} for the format of the colour array. - * - * @param float $x - * @param float $y - * @param string $text the text to write - * @param string $font the font file to use - * @param float $size the font size, in points - * @param array $color - * @param float $word_space word spacing adjustment - * @param float $char_space char spacing adjustment - * @param float $angle angle to write the text at, measured CW starting from the x-axis - */ - function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) { - $_t = "text"; - $this->_page_text[] = compact("_t", "x", "y", "text", "font", "size", "color", "word_space", "char_space", "angle"); - } - - /** - * Processes a script on every page - * - * The variables $pdf, $PAGE_NUM, and $PAGE_COUNT are available. - * - * This function can be used to add page numbers to all pages - * after the first one, for example. - * - * @param string $code the script code - * @param string $type the language type for script - */ - function page_script($code, $type = "text/php") { - $_t = "script"; - $this->_page_text[] = compact("_t", "code", "type"); - } - - function new_page() { - $this->_page_number++; - $this->_page_count++; - - $ret = $this->_pdf->newPage(); - $this->_pages[] = $ret; - return $ret; - } - - /** - * Add text to each page after rendering is complete - */ - protected function _add_page_text() { - - if ( !count($this->_page_text) ) { - return; - } - - $page_number = 1; - $eval = null; - - foreach ($this->_pages as $pid) { - $this->reopen_object($pid); - - foreach ($this->_page_text as $pt) { - extract($pt); - - switch ($_t) { - case "text": - $text = str_replace(array("{PAGE_NUM}","{PAGE_COUNT}"), - array($page_number, $this->_page_count), $text); - $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle); - break; - - case "script": - if ( !$eval ) { - $eval = new PHP_Evaluator($this); - } - $eval->evaluate($code, array('PAGE_NUM' => $page_number, 'PAGE_COUNT' => $this->_page_count)); - break; - } - } - - $this->close_object(); - $page_number++; - } - } - - /** - * Streams the PDF directly to the browser - * - * @param string $filename the name of the PDF file - * @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0 - */ - function stream($filename, $options = null) { - // Add page text - $this->_add_page_text(); - - $options["Content-Disposition"] = $filename; - $this->_pdf->stream($options); - } - - /** - * Returns the PDF as a string - * - * @param array $options Output options - * @return string - */ - function output($options = null) { - $this->_add_page_text(); - - $debug = isset($options["compress"]) && $options["compress"] != 1; - - return $this->_pdf->output($debug); - } - - /** - * Returns logging messages generated by the Cpdf class - * - * @return string - */ - function get_messages() { - return $this->_pdf->messages; - } -} diff --git a/library/vendor/dompdf/include/css_color.cls.php b/library/vendor/dompdf/include/css_color.cls.php deleted file mode 100644 index 481751db5..000000000 --- a/library/vendor/dompdf/include/css_color.cls.php +++ /dev/null @@ -1,287 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -class CSS_Color { - static $cssColorNames = array( - "aliceblue" => "F0F8FF", - "antiquewhite" => "FAEBD7", - "aqua" => "00FFFF", - "aquamarine" => "7FFFD4", - "azure" => "F0FFFF", - "beige" => "F5F5DC", - "bisque" => "FFE4C4", - "black" => "000000", - "blanchedalmond" => "FFEBCD", - "blue" => "0000FF", - "blueviolet" => "8A2BE2", - "brown" => "A52A2A", - "burlywood" => "DEB887", - "cadetblue" => "5F9EA0", - "chartreuse" => "7FFF00", - "chocolate" => "D2691E", - "coral" => "FF7F50", - "cornflowerblue" => "6495ED", - "cornsilk" => "FFF8DC", - "crimson" => "DC143C", - "cyan" => "00FFFF", - "darkblue" => "00008B", - "darkcyan" => "008B8B", - "darkgoldenrod" => "B8860B", - "darkgray" => "A9A9A9", - "darkgreen" => "006400", - "darkgrey" => "A9A9A9", - "darkkhaki" => "BDB76B", - "darkmagenta" => "8B008B", - "darkolivegreen" => "556B2F", - "darkorange" => "FF8C00", - "darkorchid" => "9932CC", - "darkred" => "8B0000", - "darksalmon" => "E9967A", - "darkseagreen" => "8FBC8F", - "darkslateblue" => "483D8B", - "darkslategray" => "2F4F4F", - "darkslategrey" => "2F4F4F", - "darkturquoise" => "00CED1", - "darkviolet" => "9400D3", - "deeppink" => "FF1493", - "deepskyblue" => "00BFFF", - "dimgray" => "696969", - "dimgrey" => "696969", - "dodgerblue" => "1E90FF", - "firebrick" => "B22222", - "floralwhite" => "FFFAF0", - "forestgreen" => "228B22", - "fuchsia" => "FF00FF", - "gainsboro" => "DCDCDC", - "ghostwhite" => "F8F8FF", - "gold" => "FFD700", - "goldenrod" => "DAA520", - "gray" => "808080", - "green" => "008000", - "greenyellow" => "ADFF2F", - "grey" => "808080", - "honeydew" => "F0FFF0", - "hotpink" => "FF69B4", - "indianred" => "CD5C5C", - "indigo" => "4B0082", - "ivory" => "FFFFF0", - "khaki" => "F0E68C", - "lavender" => "E6E6FA", - "lavenderblush" => "FFF0F5", - "lawngreen" => "7CFC00", - "lemonchiffon" => "FFFACD", - "lightblue" => "ADD8E6", - "lightcoral" => "F08080", - "lightcyan" => "E0FFFF", - "lightgoldenrodyellow" => "FAFAD2", - "lightgray" => "D3D3D3", - "lightgreen" => "90EE90", - "lightgrey" => "D3D3D3", - "lightpink" => "FFB6C1", - "lightsalmon" => "FFA07A", - "lightseagreen" => "20B2AA", - "lightskyblue" => "87CEFA", - "lightslategray" => "778899", - "lightslategrey" => "778899", - "lightsteelblue" => "B0C4DE", - "lightyellow" => "FFFFE0", - "lime" => "00FF00", - "limegreen" => "32CD32", - "linen" => "FAF0E6", - "magenta" => "FF00FF", - "maroon" => "800000", - "mediumaquamarine" => "66CDAA", - "mediumblue" => "0000CD", - "mediumorchid" => "BA55D3", - "mediumpurple" => "9370DB", - "mediumseagreen" => "3CB371", - "mediumslateblue" => "7B68EE", - "mediumspringgreen" => "00FA9A", - "mediumturquoise" => "48D1CC", - "mediumvioletred" => "C71585", - "midnightblue" => "191970", - "mintcream" => "F5FFFA", - "mistyrose" => "FFE4E1", - "moccasin" => "FFE4B5", - "navajowhite" => "FFDEAD", - "navy" => "000080", - "oldlace" => "FDF5E6", - "olive" => "808000", - "olivedrab" => "6B8E23", - "orange" => "FFA500", - "orangered" => "FF4500", - "orchid" => "DA70D6", - "palegoldenrod" => "EEE8AA", - "palegreen" => "98FB98", - "paleturquoise" => "AFEEEE", - "palevioletred" => "DB7093", - "papayawhip" => "FFEFD5", - "peachpuff" => "FFDAB9", - "peru" => "CD853F", - "pink" => "FFC0CB", - "plum" => "DDA0DD", - "powderblue" => "B0E0E6", - "purple" => "800080", - "red" => "FF0000", - "rosybrown" => "BC8F8F", - "royalblue" => "4169E1", - "saddlebrown" => "8B4513", - "salmon" => "FA8072", - "sandybrown" => "F4A460", - "seagreen" => "2E8B57", - "seashell" => "FFF5EE", - "sienna" => "A0522D", - "silver" => "C0C0C0", - "skyblue" => "87CEEB", - "slateblue" => "6A5ACD", - "slategray" => "708090", - "slategrey" => "708090", - "snow" => "FFFAFA", - "springgreen" => "00FF7F", - "steelblue" => "4682B4", - "tan" => "D2B48C", - "teal" => "008080", - "thistle" => "D8BFD8", - "tomato" => "FF6347", - "turquoise" => "40E0D0", - "violet" => "EE82EE", - "wheat" => "F5DEB3", - "white" => "FFFFFF", - "whitesmoke" => "F5F5F5", - "yellow" => "FFFF00", - "yellowgreen" => "9ACD32", - ); - - static function parse($color) { - if ( is_array($color) ) { - // Assume the array has the right format... - // FIXME: should/could verify this. - return $color; - } - - static $cache = array(); - - $color = strtolower($color); - - if ( isset($cache[$color]) ) { - return $cache[$color]; - } - - if ( in_array($color, array("transparent", "inherit")) ) { - return $cache[$color] = $color; - } - - if ( isset(self::$cssColorNames[$color]) ) { - return $cache[$color] = self::getArray(self::$cssColorNames[$color]); - } - - $length = mb_strlen($color); - - // #rgb format - if ( $length == 4 && $color[0] === "#" ) { - return $cache[$color] = self::getArray($color[1].$color[1].$color[2].$color[2].$color[3].$color[3]); - } - - // #rrggbb format - else if ( $length == 7 && $color[0] === "#" ) { - return $cache[$color] = self::getArray(mb_substr($color, 1, 6)); - } - - // rgb( r,g,b ) / rgbaa( r,g,b,α ) format - else if ( mb_strpos($color, "rgb") !== false ) { - $i = mb_strpos($color, "("); - $j = mb_strpos($color, ")"); - - // Bad color value - if ( $i === false || $j === false ) { - return null; - } - - $triplet = explode(",", mb_substr($color, $i+1, $j-$i-1)); - - // alpha transparency - // FIXME: not currently using transparency - $alpha = 1; - if ( count( $triplet ) == 4 ) { - $alpha = (float) ( trim( array_pop( $triplet ) ) ); - // bad value, set to fully opaque - if ( $alpha > 1 || $alpha < 0 ) { - $alpha = 1; - } - } - - if ( count($triplet) != 3 ) { - return null; - } - - foreach (array_keys($triplet) as $c) { - $triplet[$c] = trim($triplet[$c]); - - if ( $triplet[$c][mb_strlen($triplet[$c]) - 1] === "%" ) { - $triplet[$c] = round($triplet[$c] * 2.55); - } - } - - return $cache[$color] = self::getArray(vsprintf("%02X%02X%02X", $triplet)); - - } - - // cmyk( c,m,y,k ) format - // http://www.w3.org/TR/css3-gcpm/#cmyk-colors - else if ( mb_strpos($color, "cmyk") !== false ) { - $i = mb_strpos($color, "("); - $j = mb_strpos($color, ")"); - - // Bad color value - if ( $i === false || $j === false ) { - return null; - } - - $values = explode(",", mb_substr($color, $i+1, $j-$i-1)); - - if ( count($values) != 4 ) { - return null; - } - - foreach ($values as &$c) { - $c = floatval(trim($c)); - if ($c > 1.0) $c = 1.0; - if ($c < 0.0) $c = 0.0; - } - - return $cache[$color] = self::getArray($values); - } - - return null; - } - - static function getArray($color) { - $c = array(null, null, null, null, "hex" => null); - - if (is_array($color)) { - $c = $color; - $c["c"] = $c[0]; - $c["m"] = $c[1]; - $c["y"] = $c[2]; - $c["k"] = $c[3]; - $c["hex"] = "cmyk($c[0],$c[1],$c[2],$c[3])"; - } - else { - $c[0] = hexdec(mb_substr($color, 0, 2)) / 0xff; - $c[1] = hexdec(mb_substr($color, 2, 2)) / 0xff; - $c[2] = hexdec(mb_substr($color, 4, 2)) / 0xff; - $c["r"] = $c[0]; - $c["g"] = $c[1]; - $c["b"] = $c[2]; - $c["hex"] = "#$color"; - } - - return $c; - } -} diff --git a/library/vendor/dompdf/include/dompdf.cls.php b/library/vendor/dompdf/include/dompdf.cls.php deleted file mode 100644 index b798d486e..000000000 --- a/library/vendor/dompdf/include/dompdf.cls.php +++ /dev/null @@ -1,1101 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * DOMPDF - PHP5 HTML to PDF renderer - * - * DOMPDF loads HTML and does its best to render it as a PDF. It gets its - * name from the new DomDocument PHP5 extension. Source HTML is first - * parsed by a DomDocument object. DOMPDF takes the resulting DOM tree and - * attaches a {@link Frame} object to each node. {@link Frame} objects store - * positioning and layout information and each has a reference to a {@link - * Style} object. - * - * Style information is loaded and parsed (see {@link Stylesheet}) and is - * applied to the frames in the tree by using XPath. CSS selectors are - * converted into XPath queries, and the computed {@link Style} objects are - * applied to the {@link Frame}s. - * - * {@link Frame}s are then decorated (in the design pattern sense of the - * word) based on their CSS display property ({@link - * http://www.w3.org/TR/CSS21/visuren.html#propdef-display}). - * Frame_Decorators augment the basic {@link Frame} class by adding - * additional properties and methods specific to the particular type of - * {@link Frame}. For example, in the CSS layout model, block frames - * (display: block;) contain line boxes that are usually filled with text or - * other inline frames. The Block_Frame_Decorator therefore adds a $lines - * property as well as methods to add {@link Frame}s to lines and to add - * additional lines. {@link Frame}s also are attached to specific - * Positioner and {@link Frame_Reflower} objects that contain the - * positioining and layout algorithm for a specific type of frame, - * respectively. This is an application of the Strategy pattern. - * - * Layout, or reflow, proceeds recursively (post-order) starting at the root - * of the document. Space constraints (containing block width & height) are - * pushed down, and resolved positions and sizes bubble up. Thus, every - * {@link Frame} in the document tree is traversed once (except for tables - * which use a two-pass layout algorithm). If you are interested in the - * details, see the reflow() method of the Reflower classes. - * - * Rendering is relatively straightforward once layout is complete. {@link - * Frame}s are rendered using an adapted {@link Cpdf} class, originally - * written by Wayne Munro, http://www.ros.co.nz/pdf/. (Some performance - * related changes have been made to the original {@link Cpdf} class, and - * the {@link CPDF_Adapter} class provides a simple, stateless interface to - * PDF generation.) PDFLib support has now also been added, via the {@link - * PDFLib_Adapter}. - * - * - * @package dompdf - */ -class DOMPDF { - - /** - * DomDocument representing the HTML document - * - * @var DOMDocument - */ - protected $_xml; - - /** - * Frame_Tree derived from the DOM tree - * - * @var Frame_Tree - */ - protected $_tree; - - /** - * Stylesheet for the document - * - * @var Stylesheet - */ - protected $_css; - - /** - * Actual PDF renderer - * - * @var Canvas - */ - protected $_pdf; - - /** - * Desired paper size ('letter', 'legal', 'A4', etc.) - * - * @var string - */ - protected $_paper_size; - - /** - * Paper orientation ('portrait' or 'landscape') - * - * @var string - */ - protected $_paper_orientation; - - /** - * Callbacks on new page and new element - * - * @var array - */ - protected $_callbacks; - - /** - * Experimental caching capability - * - * @var string - */ - private $_cache_id; - - /** - * Base hostname - * - * Used for relative paths/urls - * @var string - */ - protected $_base_host; - - /** - * Absolute base path - * - * Used for relative paths/urls - * @var string - */ - protected $_base_path; - - /** - * Protcol used to request file (file://, http://, etc) - * - * @var string - */ - protected $_protocol; - - /** - * HTTP context created with stream_context_create() - * Will be used for file_get_contents - * - * @var resource - */ - protected $_http_context; - - /** - * Timestamp of the script start time - * - * @var int - */ - private $_start_time = null; - - /** - * The system's locale - * - * @var string - */ - private $_system_locale = null; - - /** - * Tells if the system's locale is the C standard one - * - * @var bool - */ - private $_locale_standard = false; - - /** - * The default view of the PDF in the viewer - * - * @var string - */ - private $_default_view = "Fit"; - - /** - * The default view options of the PDF in the viewer - * - * @var array - */ - private $_default_view_options = array(); - - /** - * Tells wether the DOM document is in quirksmode (experimental) - * - * @var bool - */ - private $_quirksmode = false; - - /** - * Protocol whitelist - * - * Protocols and PHP wrappers allowed in URLs. Full support is not - * guarantee for the protocols/wrappers contained in this array. - * - * @var array - */ - private $_allowed_protocols = array(null, "", "file://", "http://", "https://"); - - /** - * Local file extension whitelist - * - * File extensions supported by dompdf for local files. - * - * @var array - */ - private $_allowed_local_file_extensions = array("htm", "html"); - - /** - * The list of built-in fonts - * - * @var array - */ - public static $native_fonts = array( - "courier", "courier-bold", "courier-oblique", "courier-boldoblique", - "helvetica", "helvetica-bold", "helvetica-oblique", "helvetica-boldoblique", - "times-roman", "times-bold", "times-italic", "times-bolditalic", - "symbol", "zapfdinbats" - ); - - private $_options = array( - // Directories - "temp_dir" => DOMPDF_TEMP_DIR, - "font_dir" => DOMPDF_FONT_DIR, - "font_cache" => DOMPDF_FONT_CACHE, - "chroot" => DOMPDF_CHROOT, - "log_output_file" => DOMPDF_LOG_OUTPUT_FILE, - - // Rendering - "default_media_type" => DOMPDF_DEFAULT_MEDIA_TYPE, - "default_paper_size" => DOMPDF_DEFAULT_PAPER_SIZE, - "default_font" => DOMPDF_DEFAULT_FONT, - "dpi" => DOMPDF_DPI, - "font_height_ratio" => DOMPDF_FONT_HEIGHT_RATIO, - - // Features - "enable_unicode" => DOMPDF_UNICODE_ENABLED, - "enable_php" => DOMPDF_ENABLE_PHP, - "enable_remote" => DOMPDF_ENABLE_REMOTE, - "enable_css_float" => DOMPDF_ENABLE_CSS_FLOAT, - "enable_javascript" => DOMPDF_ENABLE_JAVASCRIPT, - "enable_html5_parser" => DOMPDF_ENABLE_HTML5PARSER, - "enable_font_subsetting" => DOMPDF_ENABLE_FONTSUBSETTING, - - // Debug - "debug_png" => DEBUGPNG, - "debug_keep_temp" => DEBUGKEEPTEMP, - "debug_css" => DEBUGCSS, - "debug_layout" => DEBUG_LAYOUT, - "debug_layout_lines" => DEBUG_LAYOUT_LINES, - "debug_layout_blocks" => DEBUG_LAYOUT_BLOCKS, - "debug_layout_inline" => DEBUG_LAYOUT_INLINE, - "debug_layout_padding_box" => DEBUG_LAYOUT_PADDINGBOX, - - // Admin - "admin_username" => DOMPDF_ADMIN_USERNAME, - "admin_password" => DOMPDF_ADMIN_PASSWORD, - ); - - /** - * Class constructor - */ - function __construct() { - $this->_locale_standard = sprintf('%.1f', 1.0) == '1.0'; - - $this->save_locale(); - - $this->_messages = array(); - $this->_css = new Stylesheet($this); - $this->_pdf = null; - $this->_paper_size = DOMPDF_DEFAULT_PAPER_SIZE; - $this->_paper_orientation = "portrait"; - $this->_base_protocol = ""; - $this->_base_host = ""; - $this->_base_path = ""; - $this->_http_context = null; - $this->_callbacks = array(); - $this->_cache_id = null; - - $this->restore_locale(); - } - - /** - * Class destructor - */ - function __destruct() { - clear_object($this); - } - - /** - * Get the dompdf option value - * - * @param string $key - * - * @return mixed - * @throws DOMPDF_Exception - */ - function get_option($key) { - if ( !array_key_exists($key, $this->_options) ) { - throw new DOMPDF_Exception("Option '$key' doesn't exist"); - } - - return $this->_options[$key]; - } - - /** - * @param string $key - * @param mixed $value - * - * @throws DOMPDF_Exception - */ - function set_option($key, $value) { - if ( !array_key_exists($key, $this->_options) ) { - throw new DOMPDF_Exception("Option '$key' doesn't exist"); - } - - $this->_options[$key] = $value; - } - - /** - * @param array $options - */ - function set_options(array $options) { - foreach ($options as $key => $value) { - $this->set_option($key, $value); - } - } - - /** - * Save the system's locale configuration and - * set the right value for numeric formatting - */ - private function save_locale() { - if ( $this->_locale_standard ) { - return; - } - - $this->_system_locale = setlocale(LC_NUMERIC, "0"); - setlocale(LC_NUMERIC, "C"); - } - - /** - * Restore the system's locale configuration - */ - private function restore_locale() { - if ( $this->_locale_standard ) { - return; - } - - setlocale(LC_NUMERIC, $this->_system_locale); - } - - /** - * Returns the underlying {@link Frame_Tree} object - * - * @return Frame_Tree - */ - function get_tree() { - return $this->_tree; - } - - /** - * Sets the protocol to use - * FIXME validate these - * - * @param string $proto - */ - function set_protocol($proto) { - $this->_protocol = $proto; - } - - /** - * Sets the base hostname - * - * @param string $host - */ - function set_host($host) { - $this->_base_host = $host; - } - - /** - * Sets the base path - * - * @param string $path - */ - function set_base_path($path) { - $this->_base_path = $path; - } - - /** - * Sets the HTTP context - * - * @param resource $http_context - */ - function set_http_context($http_context) { - $this->_http_context = $http_context; - } - - /** - * Sets the default view - * - * @param string $default_view The default document view - * @param array $options The view's options - */ - function set_default_view($default_view, $options) { - $this->_default_view = $default_view; - $this->_default_view_options = $options; - } - - /** - * Returns the protocol in use - * - * @return string - */ - function get_protocol() { - return $this->_protocol; - } - - /** - * Returns the base hostname - * - * @return string - */ - function get_host() { - return $this->_base_host; - } - - /** - * Returns the base path - * - * @return string - */ - function get_base_path() { - return $this->_base_path; - } - - /** - * Returns the HTTP context - * - * @return resource - */ - function get_http_context() { - return $this->_http_context; - } - - /** - * Return the underlying Canvas instance (e.g. CPDF_Adapter, GD_Adapter) - * - * @return Canvas - */ - function get_canvas() { - return $this->_pdf; - } - - /** - * Returns the callbacks array - * - * @return array - */ - function get_callbacks() { - return $this->_callbacks; - } - - /** - * Returns the stylesheet - * - * @return Stylesheet - */ - function get_css() { - return $this->_css; - } - - /** - * @return DOMDocument - */ - function get_dom() { - return $this->_xml; - } - - /** - * Loads an HTML file - * Parse errors are stored in the global array _dompdf_warnings. - * - * @param string $file a filename or url to load - * - * @throws DOMPDF_Exception - */ - function load_html_file($file) { - $this->save_locale(); - - // Store parsing warnings as messages (this is to prevent output to the - // browser if the html is ugly and the dom extension complains, - // preventing the pdf from being streamed.) - if ( !$this->_protocol && !$this->_base_host && !$this->_base_path ) { - list($this->_protocol, $this->_base_host, $this->_base_path) = explode_url($file); - } - - if ( !in_array($this->_protocol, $this->_allowed_protocols) ) { - throw new DOMPDF_Exception("Permission denied on $file. The communication protocol is not supported."); - } - - if ( !$this->get_option("enable_remote") && ($this->_protocol != "" && $this->_protocol !== "file://" ) ) { - throw new DOMPDF_Exception("Remote file requested, but DOMPDF_ENABLE_REMOTE is false."); - } - - if ($this->_protocol == "" || $this->_protocol === "file://") { - - // Get the full path to $file, returns false if the file doesn't exist - $realfile = realpath($file); - - $chroot = $this->get_option("chroot"); - if ( strpos($realfile, $chroot) !== 0 ) { - throw new DOMPDF_Exception("Permission denied on $file. The file could not be found under the directory specified by DOMPDF_CHROOT."); - } - - $ext = pathinfo($realfile, PATHINFO_EXTENSION); - if (!in_array($ext, $this->_allowed_local_file_extensions)) { - throw new DOMPDF_Exception("Permission denied on $file."); - } - - if ( !$realfile ) { - throw new DOMPDF_Exception("File '$file' not found."); - } - - $file = $realfile; - } - - $contents = file_get_contents($file, null, $this->_http_context); - $encoding = null; - - // See http://the-stickman.com/web-development/php/getting-http-response-headers-when-using-file_get_contents/ - if ( isset($http_response_header) ) { - foreach($http_response_header as $_header) { - if ( preg_match("@Content-Type:\s*[\w/]+;\s*?charset=([^\s]+)@i", $_header, $matches) ) { - $encoding = strtoupper($matches[1]); - break; - } - } - } - - $this->restore_locale(); - - $this->load_html($contents, $encoding); - } - - /** - * Loads an HTML string - * Parse errors are stored in the global array _dompdf_warnings. - * @todo use the $encoding variable - * - * @param string $str HTML text to load - * @param string $encoding Not used yet - */ - function load_html($str, $encoding = null) { - $this->save_locale(); - - // FIXME: Determine character encoding, switch to UTF8, update meta tag. Need better http/file stream encoding detection, currently relies on text or meta tag. - mb_detect_order('auto'); - - if (mb_detect_encoding($str) !== 'UTF-8') { - $metatags = array( - '@]*charset\s*=\s*["\']?\s*([^"\' ]+)@i', - ); - - foreach($metatags as $metatag) { - if (preg_match($metatag, $str, $matches)) break; - } - - if (mb_detect_encoding($str) == '') { - if (isset($matches[1])) { - $encoding = strtoupper($matches[1]); - } - else { - $encoding = 'UTF-8'; - } - } - else { - if ( isset($matches[1]) ) { - $encoding = strtoupper($matches[1]); - } - else { - $encoding = 'auto'; - } - } - - if ( $encoding !== 'UTF-8' ) { - $str = mb_convert_encoding($str, 'UTF-8', $encoding); - } - - if ( isset($matches[1]) ) { - $str = preg_replace('/charset=([^\s"]+)/i', 'charset=UTF-8', $str); - } - else { - $str = str_replace('', '', $str); - } - } - else { - $encoding = 'UTF-8'; - } - - // remove BOM mark from UTF-8, it's treated as document text by DOMDocument - // FIXME: roll this into the encoding detection using UTF-8/16/32 BOM (http://us2.php.net/manual/en/function.mb-detect-encoding.php#91051)? - if ( substr($str, 0, 3) == chr(0xEF).chr(0xBB).chr(0xBF) ) { - $str = substr($str, 3); - } - - // if the document contains non utf-8 with a utf-8 meta tag chars and was - // detected as utf-8 by mbstring, problems could happen. - // http://devzone.zend.com/article/8855 - if ( $encoding !== 'UTF-8' ) { - $re = '/]*)((?:charset=[^"\' ]+)([^>]*)|(?:charset=["\'][^"\' ]+["\']))([^>]*)>/i'; - $str = preg_replace($re, '', $str); - } - - // Store parsing warnings as messages - set_error_handler("record_warnings"); - - // @todo Take the quirksmode into account - // http://hsivonen.iki.fi/doctype/ - // https://developer.mozilla.org/en/mozilla's_quirks_mode - $quirksmode = false; - - if ( $this->get_option("enable_html5_parser") ) { - $tokenizer = new HTML5_Tokenizer($str); - $tokenizer->parse(); - $doc = $tokenizer->save(); - - // Remove #text children nodes in nodes that shouldn't have - $tag_names = array("html", "table", "tbody", "thead", "tfoot", "tr"); - foreach($tag_names as $tag_name) { - $nodes = $doc->getElementsByTagName($tag_name); - - foreach($nodes as $node) { - self::remove_text_nodes($node); - } - } - - $quirksmode = ($tokenizer->getTree()->getQuirksMode() > HTML5_TreeBuilder::NO_QUIRKS); - } - else { - // loadHTML assumes ISO-8859-1 unless otherwise specified, but there are - // bugs in how DOMDocument determines the actual encoding. Converting to - // HTML-ENTITIES prior to import appears to resolve the issue. - // http://devzone.zend.com/1538/php-dom-xml-extension-encoding-processing/ (see #4) - // http://stackoverflow.com/a/11310258/264628 - $doc = new DOMDocument(); - $doc->preserveWhiteSpace = true; - $doc->loadHTML( mb_convert_encoding( $str , 'HTML-ENTITIES' , 'UTF-8' ) ); - - // If some text is before the doctype, we are in quirksmode - if ( preg_match("/^(.+) - if ( !$doc->doctype->publicId && !$doc->doctype->systemId ) { - $quirksmode = false; - } - - // not XHTML - if ( !preg_match("/xhtml/i", $doc->doctype->publicId) ) { - $quirksmode = true; - } - } - } - - $this->_xml = $doc; - $this->_quirksmode = $quirksmode; - - $this->_tree = new Frame_Tree($this->_xml); - - restore_error_handler(); - - $this->restore_locale(); - } - - static function remove_text_nodes(DOMNode $node) { - $children = array(); - for ($i = 0; $i < $node->childNodes->length; $i++) { - $child = $node->childNodes->item($i); - if ( $child->nodeName === "#text" ) { - $children[] = $child; - } - } - - foreach($children as $child) { - $node->removeChild($child); - } - } - - /** - * Builds the {@link Frame_Tree}, loads any CSS and applies the styles to - * the {@link Frame_Tree} - */ - protected function _process_html() { - $this->_tree->build_tree(); - - $this->_css->load_css_file(Stylesheet::DEFAULT_STYLESHEET, Stylesheet::ORIG_UA); - - $acceptedmedia = Stylesheet::$ACCEPTED_GENERIC_MEDIA_TYPES; - $acceptedmedia[] = $this->get_option("default_media_type"); - - // - $base_nodes = $this->_xml->getElementsByTagName("base"); - if ( $base_nodes->length && ($href = $base_nodes->item(0)->getAttribute("href")) ) { - list($this->_protocol, $this->_base_host, $this->_base_path) = explode_url($href); - } - - // Set the base path of the Stylesheet to that of the file being processed - $this->_css->set_protocol($this->_protocol); - $this->_css->set_host($this->_base_host); - $this->_css->set_base_path($this->_base_path); - - // Get all the stylesheets so that they are processed in document order - $xpath = new DOMXPath($this->_xml); - $stylesheets = $xpath->query("//*[name() = 'link' or name() = 'style']"); - - foreach($stylesheets as $tag) { - switch (strtolower($tag->nodeName)) { - // load tags - case "link": - if ( mb_strtolower(stripos($tag->getAttribute("rel"), "stylesheet") !== false) || // may be "appendix stylesheet" - mb_strtolower($tag->getAttribute("type")) === "text/css" ) { - //Check if the css file is for an accepted media type - //media not given then always valid - $formedialist = preg_split("/[\s\n,]/", $tag->getAttribute("media"),-1, PREG_SPLIT_NO_EMPTY); - if ( count($formedialist) > 0 ) { - $accept = false; - foreach ( $formedialist as $type ) { - if ( in_array(mb_strtolower(trim($type)), $acceptedmedia) ) { - $accept = true; - break; - } - } - - if (!$accept) { - //found at least one mediatype, but none of the accepted ones - //Skip this css file. - continue; - } - } - - $url = $tag->getAttribute("href"); - $url = build_url($this->_protocol, $this->_base_host, $this->_base_path, $url); - - $this->_css->load_css_file($url, Stylesheet::ORIG_AUTHOR); - } - break; - - // load - $child = $child->nextSibling; - } - } - else { - $css = $tag->nodeValue; - } - - $this->_css->load_css($css); - break; - } - } - } - - /** - * Sets the paper size & orientation - * - * @param string $size 'letter', 'legal', 'A4', etc. {@link CPDF_Adapter::$PAPER_SIZES} - * @param string $orientation 'portrait' or 'landscape' - */ - function set_paper($size, $orientation = "portrait") { - $this->_paper_size = $size; - $this->_paper_orientation = $orientation; - } - - /** - * Enable experimental caching capability - * @access private - */ - function enable_caching($cache_id) { - $this->_cache_id = $cache_id; - } - - /** - * Sets callbacks for events like rendering of pages and elements. - * The callbacks array contains arrays with 'event' set to 'begin_page', - * 'end_page', 'begin_frame', or 'end_frame' and 'f' set to a function or - * object plus method to be called. - * - * The function 'f' must take an array as argument, which contains info - * about the event. - * - * @param array $callbacks the set of callbacks to set - */ - function set_callbacks($callbacks) { - if (is_array($callbacks)) { - $this->_callbacks = array(); - foreach ($callbacks as $c) { - if (is_array($c) && isset($c['event']) && isset($c['f'])) { - $event = $c['event']; - $f = $c['f']; - if (is_callable($f) && is_string($event)) { - $this->_callbacks[$event][] = $f; - } - } - } - } - } - - /** - * Get the quirks mode - * - * @return boolean true if quirks mode is active - */ - function get_quirksmode(){ - return $this->_quirksmode; - } - - function parse_default_view($value) { - $valid = array("XYZ", "Fit", "FitH", "FitV", "FitR", "FitB", "FitBH", "FitBV"); - - $options = preg_split("/\s*,\s*/", trim($value)); - $default_view = array_shift($options); - - if ( !in_array($default_view, $valid) ) { - return false; - } - - $this->set_default_view($default_view, $options); - return true; - } - - /** - * Renders the HTML to PDF - */ - function render() { - $this->save_locale(); - - $log_output_file = $this->get_option("log_output_file"); - if ( $log_output_file ) { - if ( !file_exists($log_output_file) && is_writable(dirname($log_output_file)) ) { - touch($log_output_file); - } - - $this->_start_time = microtime(true); - ob_start(); - } - - //enable_mem_profile(); - - $this->_process_html(); - - $this->_css->apply_styles($this->_tree); - - // @page style rules : size, margins - $page_styles = $this->_css->get_page_styles(); - - $base_page_style = $page_styles["base"]; - unset($page_styles["base"]); - - foreach($page_styles as $_page_style) { - $_page_style->inherit($base_page_style); - } - - if ( is_array($base_page_style->size) ) { - $this->set_paper(array(0, 0, $base_page_style->size[0], $base_page_style->size[1])); - } - - $this->_pdf = Canvas_Factory::get_instance($this, $this->_paper_size, $this->_paper_orientation); - Font_Metrics::init($this->_pdf); - - if ( $this->get_option("enable_font_subsetting") && $this->_pdf instanceof CPDF_Adapter ) { - foreach ($this->_tree->get_frames() as $frame) { - $style = $frame->get_style(); - $node = $frame->get_node(); - - // Handle text nodes - if ( $node->nodeName === "#text" ) { - $this->_pdf->register_string_subset($style->font_family, $node->nodeValue); - continue; - } - - // Handle generated content (list items) - if ( $style->display === "list-item" ) { - $chars = List_Bullet_Renderer::get_counter_chars($style->list_style_type); - $this->_pdf->register_string_subset($style->font_family, $chars); - continue; - } - - // Handle other generated content (pseudo elements) - // FIXME: This only captures the text of the stylesheet declaration, - // not the actual generated content, and forces all possible counter - // values. See notes in issue #750. - if ( $frame->get_node()->nodeName == "dompdf_generated" ) { - // all possible counter values - $chars = List_Bullet_Renderer::get_counter_chars('decimal'); - $this->_pdf->register_string_subset($style->font_family, $chars); - $chars = List_Bullet_Renderer::get_counter_chars('upper-alpha'); - $this->_pdf->register_string_subset($style->font_family, $chars); - $chars = List_Bullet_Renderer::get_counter_chars('lower-alpha'); - $this->_pdf->register_string_subset($style->font_family, $chars); - $chars = List_Bullet_Renderer::get_counter_chars('lower-greek'); - $this->_pdf->register_string_subset($style->font_family, $chars); - // the text of the stylesheet declaration - $this->_pdf->register_string_subset($style->font_family, $style->content); - continue; - } - } - } - - $root = null; - - foreach ($this->_tree->get_frames() as $frame) { - // Set up the root frame - if ( is_null($root) ) { - $root = Frame_Factory::decorate_root( $this->_tree->get_root(), $this ); - continue; - } - - // Create the appropriate decorators, reflowers & positioners. - Frame_Factory::decorate_frame($frame, $this, $root); - } - - // Add meta information - $title = $this->_xml->getElementsByTagName("title"); - if ( $title->length ) { - $this->_pdf->add_info("Title", trim($title->item(0)->nodeValue)); - } - - $metas = $this->_xml->getElementsByTagName("meta"); - $labels = array( - "author" => "Author", - "keywords" => "Keywords", - "description" => "Subject", - ); - foreach($metas as $meta) { - $name = mb_strtolower($meta->getAttribute("name")); - $value = trim($meta->getAttribute("content")); - - if ( isset($labels[$name]) ) { - $this->_pdf->add_info($labels[$name], $value); - continue; - } - - if ( $name === "dompdf.view" && $this->parse_default_view($value) ) { - $this->_pdf->set_default_view($this->_default_view, $this->_default_view_options); - } - } - - $root->set_containing_block(0, 0, $this->_pdf->get_width(), $this->_pdf->get_height()); - $root->set_renderer(new Renderer($this)); - - // This is where the magic happens: - $root->reflow(); - - // Clean up cached images - Image_Cache::clear(); - - global $_dompdf_warnings, $_dompdf_show_warnings; - if ( $_dompdf_show_warnings ) { - echo 'DOMPDF Warnings
';
-      foreach ($_dompdf_warnings as $msg) {
-        echo $msg . "\n";
-      }
-      echo $this->get_canvas()->get_cpdf()->messages;
-      echo '
'; - flush(); - } - - $this->restore_locale(); - } - - /** - * Add meta information to the PDF after rendering - */ - function add_info($label, $value) { - if ( !is_null($this->_pdf) ) { - $this->_pdf->add_info($label, $value); - } - } - - /** - * Writes the output buffer in the log file - * - * @return void - */ - private function write_log() { - $log_output_file = $this->get_option("log_output_file"); - if ( !$log_output_file || !is_writable($log_output_file) ) { - return; - } - - $frames = Frame::$ID_COUNTER; - $memory = DOMPDF_memory_usage() / 1024; - $time = (microtime(true) - $this->_start_time) * 1000; - - $out = sprintf( - "%6d". - "%10.2f KB". - "%10.2f ms". - " ". - ($this->_quirksmode ? " ON" : "OFF"). - "
", $frames, $memory, $time); - - $out .= ob_get_clean(); - - $log_output_file = $this->get_option("log_output_file"); - file_put_contents($log_output_file, $out); - } - - /** - * Streams the PDF to the client - * - * The file will open a download dialog by default. The options - * parameter controls the output. Accepted options are: - * - * 'Accept-Ranges' => 1 or 0 - if this is not set to 1, then this - * header is not included, off by default this header seems to - * have caused some problems despite the fact that it is supposed - * to solve them, so I am leaving it off by default. - * - * 'compress' = > 1 or 0 - apply content stream compression, this is - * on (1) by default - * - * 'Attachment' => 1 or 0 - if 1, force the browser to open a - * download dialog, on (1) by default - * - * @param string $filename the name of the streamed file - * @param array $options header options (see above) - */ - function stream($filename, $options = null) { - $this->save_locale(); - - $this->write_log(); - - if ( !is_null($this->_pdf) ) { - $this->_pdf->stream($filename, $options); - } - - $this->restore_locale(); - } - - /** - * Returns the PDF as a string - * - * The file will open a download dialog by default. The options - * parameter controls the output. Accepted options are: - * - * - * 'compress' = > 1 or 0 - apply content stream compression, this is - * on (1) by default - * - * - * @param array $options options (see above) - * - * @return string - */ - function output($options = null) { - $this->save_locale(); - - $this->write_log(); - - if ( is_null($this->_pdf) ) { - return null; - } - - $output = $this->_pdf->output( $options ); - - $this->restore_locale(); - - return $output; - } - - /** - * Returns the underlying HTML document as a string - * - * @return string - */ - function output_html() { - return $this->_xml->saveHTML(); - } -} diff --git a/library/vendor/dompdf/include/dompdf_exception.cls.php b/library/vendor/dompdf/include/dompdf_exception.cls.php deleted file mode 100644 index ca47fa036..000000000 --- a/library/vendor/dompdf/include/dompdf_exception.cls.php +++ /dev/null @@ -1,26 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Standard exception thrown by DOMPDF classes - * - * @package dompdf - */ -class DOMPDF_Exception extends Exception { - - /** - * Class constructor - * - * @param string $message Error message - * @param int $code Error code - */ - function __construct($message = null, $code = 0) { - parent::__construct($message, $code); - } - -} diff --git a/library/vendor/dompdf/include/dompdf_image_exception.cls.php b/library/vendor/dompdf/include/dompdf_image_exception.cls.php deleted file mode 100644 index 8fdecec52..000000000 --- a/library/vendor/dompdf/include/dompdf_image_exception.cls.php +++ /dev/null @@ -1,26 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Image exception thrown by DOMPDF - * - * @package dompdf - */ -class DOMPDF_Image_Exception extends DOMPDF_Exception { - - /** - * Class constructor - * - * @param string $message Error message - * @param int $code Error code - */ - function __construct($message = null, $code = 0) { - parent::__construct($message, $code); - } - -} diff --git a/library/vendor/dompdf/include/fixed_positioner.cls.php b/library/vendor/dompdf/include/fixed_positioner.cls.php deleted file mode 100644 index 31a2a079c..000000000 --- a/library/vendor/dompdf/include/fixed_positioner.cls.php +++ /dev/null @@ -1,88 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Positions fixely positioned frames - */ -class Fixed_Positioner extends Positioner { - - function __construct(Frame_Decorator $frame) { parent::__construct($frame); } - - function position() { - - $frame = $this->_frame; - $style = $frame->get_original_style(); - $root = $frame->get_root(); - $initialcb = $root->get_containing_block(); - $initialcb_style = $root->get_style(); - - $p = $frame->find_block_parent(); - if ( $p ) { - $p->add_line(); - } - - // Compute the margins of the @page style - $margin_top = $initialcb_style->length_in_pt($initialcb_style->margin_top, $initialcb["h"]); - $margin_right = $initialcb_style->length_in_pt($initialcb_style->margin_right, $initialcb["w"]); - $margin_bottom = $initialcb_style->length_in_pt($initialcb_style->margin_bottom, $initialcb["h"]); - $margin_left = $initialcb_style->length_in_pt($initialcb_style->margin_left, $initialcb["w"]); - - // The needed computed style of the element - $height = $style->length_in_pt($style->height, $initialcb["h"]); - $width = $style->length_in_pt($style->width, $initialcb["w"]); - - $top = $style->length_in_pt($style->top, $initialcb["h"]); - $right = $style->length_in_pt($style->right, $initialcb["w"]); - $bottom = $style->length_in_pt($style->bottom, $initialcb["h"]); - $left = $style->length_in_pt($style->left, $initialcb["w"]); - - $y = $margin_top; - if ( isset($top) ) { - $y = $top + $margin_top; - if ( $top === "auto" ) { - $y = $margin_top; - if ( isset($bottom) && $bottom !== "auto" ) { - $y = $initialcb["h"] - $bottom - $margin_bottom; - $margin_height = $this->_frame->get_margin_height(); - if ( $margin_height !== "auto" ) { - $y -= $margin_height; - } - else { - $y -= $height; - } - } - } - } - - $x = $margin_left; - if ( isset($left) ) { - $x = $left + $margin_left; - if ( $left === "auto" ) { - $x = $margin_left; - if ( isset($right) && $right !== "auto" ) { - $x = $initialcb["w"] - $right - $margin_right; - $margin_width = $this->_frame->get_margin_width(); - if ( $margin_width !== "auto" ) { - $x -= $margin_width; - } - else { - $x -= $width; - } - } - } - } - - $frame->set_position($x, $y); - - $children = $frame->get_children(); - foreach($children as $child) { - $child->set_position($x, $y); - } - } -} \ No newline at end of file diff --git a/library/vendor/dompdf/include/font_metrics.cls.php b/library/vendor/dompdf/include/font_metrics.cls.php deleted file mode 100644 index 2d15d6714..000000000 --- a/library/vendor/dompdf/include/font_metrics.cls.php +++ /dev/null @@ -1,382 +0,0 @@ - - * @author Helmut Tischer - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -require_once DOMPDF_LIB_DIR . "/class.pdf.php"; - -/** - * Name of the font cache file - * - * This file must be writable by the webserver process only to update it - * with save_font_families() after adding the .afm file references of a new font family - * with Font_Metrics::save_font_families(). - * This is typically done only from command line with load_font.php on converting - * ttf fonts to ufm with php-font-lib. - * - * Declared here because PHP5 prevents constants from being declared with expressions - */ -define('__DOMPDF_FONT_CACHE_FILE', DOMPDF_FONT_DIR . "dompdf_font_family_cache.php"); - -/** - * The font metrics class - * - * This class provides information about fonts and text. It can resolve - * font names into actual installed font files, as well as determine the - * size of text in a particular font and size. - * - * @static - * @package dompdf - */ -class Font_Metrics { - - /** - * @see __DOMPDF_FONT_CACHE_FILE - */ - const CACHE_FILE = __DOMPDF_FONT_CACHE_FILE; - - /** - * Underlying {@link Canvas} object to perform text size calculations - * - * @var Canvas - */ - static protected $_pdf = null; - - /** - * Array of font family names to font files - * - * Usually cached by the {@link load_font.php} script - * - * @var array - */ - static protected $_font_lookup = array(); - - - /** - * Class initialization - * - */ - static function init(Canvas $canvas = null) { - if (!self::$_pdf) { - if (!$canvas) { - $canvas = Canvas_Factory::get_instance(new DOMPDF()); - } - - self::$_pdf = $canvas; - } - } - - /** - * Calculates text size, in points - * - * @param string $text the text to be sized - * @param string $font the desired font - * @param float $size the desired font size - * @param float $word_spacing - * @param float $char_spacing - * - * @internal param float $spacing word spacing, if any - * @return float - */ - static function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) { - //return self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing); - - // @todo Make sure this cache is efficient before enabling it - static $cache = array(); - - if ( $text === "" ) { - return 0; - } - - // Don't cache long strings - $use_cache = !isset($text[50]); // Faster than strlen - - $key = "$font/$size/$word_spacing/$char_spacing"; - - if ( $use_cache && isset($cache[$key][$text]) ) { - return $cache[$key]["$text"]; - } - - $width = self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing); - - if ( $use_cache ) { - $cache[$key][$text] = $width; - } - - return $width; - } - - /** - * Calculates font height - * - * @param string $font - * @param float $size - * @return float - */ - static function get_font_height($font, $size) { - return self::$_pdf->get_font_height($font, $size); - } - - /** - * Resolves a font family & subtype into an actual font file - * Subtype can be one of 'normal', 'bold', 'italic' or 'bold_italic'. If - * the particular font family has no suitable font file, the default font - * ({@link DOMPDF_DEFAULT_FONT}) is used. The font file returned - * is the absolute pathname to the font file on the system. - * - * @param string $family_raw - * @param string $subtype_raw - * - * @return string - */ - static function get_font($family_raw, $subtype_raw = "normal") { - static $cache = array(); - - if ( isset($cache[$family_raw][$subtype_raw]) ) { - return $cache[$family_raw][$subtype_raw]; - } - - /* Allow calling for various fonts in search path. Therefore not immediately - * return replacement on non match. - * Only when called with NULL try replacement. - * When this is also missing there is really trouble. - * If only the subtype fails, nevertheless return failure. - * Only on checking the fallback font, check various subtypes on same font. - */ - - $subtype = strtolower($subtype_raw); - - if ( $family_raw ) { - $family = str_replace( array("'", '"'), "", strtolower($family_raw)); - - if ( isset(self::$_font_lookup[$family][$subtype]) ) { - return $cache[$family_raw][$subtype_raw] = self::$_font_lookup[$family][$subtype]; - } - - return null; - } - - $family = "serif"; - - if ( isset(self::$_font_lookup[$family][$subtype]) ) { - return $cache[$family_raw][$subtype_raw] = self::$_font_lookup[$family][$subtype]; - } - - if ( !isset(self::$_font_lookup[$family]) ) { - return null; - } - - $family = self::$_font_lookup[$family]; - - foreach ( $family as $sub => $font ) { - if (strpos($subtype, $sub) !== false) { - return $cache[$family_raw][$subtype_raw] = $font; - } - } - - if ($subtype !== "normal") { - foreach ( $family as $sub => $font ) { - if ($sub !== "normal") { - return $cache[$family_raw][$subtype_raw] = $font; - } - } - } - - $subtype = "normal"; - - if ( isset($family[$subtype]) ) { - return $cache[$family_raw][$subtype_raw] = $family[$subtype]; - } - - return null; - } - - static function get_family($family) { - $family = str_replace( array("'", '"'), "", mb_strtolower($family)); - - if ( isset(self::$_font_lookup[$family]) ) { - return self::$_font_lookup[$family]; - } - - return null; - } - - /** - * Saves the stored font family cache - * - * The name and location of the cache file are determined by {@link - * Font_Metrics::CACHE_FILE}. This file should be writable by the - * webserver process. - * - * @see Font_Metrics::load_font_families() - */ - static function save_font_families() { - // replace the path to the DOMPDF font directories with the corresponding constants (allows for more portability) - $cache_data = sprintf(" $variants) { - $cache_data .= sprintf(" '%s' => array(%s", addslashes($family), PHP_EOL); - foreach ($variants as $variant => $path) { - $path = sprintf("'%s'", $path); - $path = str_replace('\'' . DOMPDF_FONT_DIR , 'DOMPDF_FONT_DIR . \'' , $path); - $path = str_replace('\'' . DOMPDF_DIR , 'DOMPDF_DIR . \'' , $path); - $cache_data .= sprintf(" '%s' => %s,%s", $variant, $path, PHP_EOL); - } - $cache_data .= sprintf(" ),%s", PHP_EOL); - } - $cache_data .= ") ?>"; - file_put_contents(self::CACHE_FILE, $cache_data); - } - - /** - * Loads the stored font family cache - * - * @see save_font_families() - */ - static function load_font_families() { - $dist_fonts = require_once DOMPDF_DIR . "/lib/fonts/dompdf_font_family_cache.dist.php"; - - // FIXME: temporary step for font cache created before the font cache fix - if ( is_readable( DOMPDF_FONT_DIR . "dompdf_font_family_cache" ) ) { - $old_fonts = require_once DOMPDF_FONT_DIR . "dompdf_font_family_cache"; - // If the font family cache is still in the old format - if ( $old_fonts === 1 ) { - $cache_data = file_get_contents(DOMPDF_FONT_DIR . "dompdf_font_family_cache"); - file_put_contents(DOMPDF_FONT_DIR . "dompdf_font_family_cache", "<"."?php return $cache_data ?".">"); - $old_fonts = require_once DOMPDF_FONT_DIR . "dompdf_font_family_cache"; - } - $dist_fonts += $old_fonts; - } - - if ( !is_readable(self::CACHE_FILE) ) { - self::$_font_lookup = $dist_fonts; - return; - } - - $cache_data = require_once self::CACHE_FILE; - - // If the font family cache is still in the old format - if ( self::$_font_lookup === 1 ) { - $cache_data = file_get_contents(self::CACHE_FILE); - file_put_contents(self::CACHE_FILE, "<"."?php return $cache_data ?".">"); - $cache_data = require_once self::CACHE_FILE; - } - - self::$_font_lookup = array(); - foreach ($cache_data as $key => $value) { - self::$_font_lookup[stripslashes($key)] = $value; - } - - // Merge provided fonts - self::$_font_lookup += $dist_fonts; - } - - static function get_type($type) { - if (preg_match("/bold/i", $type)) { - if (preg_match("/italic|oblique/i", $type)) { - $type = "bold_italic"; - } - else { - $type = "bold"; - } - } - elseif (preg_match("/italic|oblique/i", $type)) { - $type = "italic"; - } - else { - $type = "normal"; - } - - return $type; - } - - static function install_fonts($files) { - $names = array(); - - foreach($files as $file) { - $font = Font::load($file); - $records = $font->getData("name", "records"); - $type = self::get_type($records[2]); - $names[mb_strtolower($records[1])][$type] = $file; - } - - return $names; - } - - static function get_system_fonts() { - $files = glob("/usr/share/fonts/truetype/*.ttf") + - glob("/usr/share/fonts/truetype/*/*.ttf") + - glob("/usr/share/fonts/truetype/*/*/*.ttf") + - glob("C:\\Windows\\fonts\\*.ttf") + - glob("C:\\WinNT\\fonts\\*.ttf") + - glob("/mnt/c_drive/WINDOWS/Fonts/"); - - return self::install_fonts($files); - } - - /** - * Returns the current font lookup table - * - * @return array - */ - static function get_font_families() { - return self::$_font_lookup; - } - - static function set_font_family($fontname, $entry) { - self::$_font_lookup[mb_strtolower($fontname)] = $entry; - } - - static function register_font($style, $remote_file, $context = null) { - $fontname = mb_strtolower($style["family"]); - $families = Font_Metrics::get_font_families(); - - $entry = array(); - if ( isset($families[$fontname]) ) { - $entry = $families[$fontname]; - } - - $local_file = DOMPDF_FONT_DIR . md5($remote_file); - $local_temp_file = DOMPDF_TEMP_DIR . "/" . md5($remote_file); - $cache_entry = $local_file; - $local_file .= ".ttf"; - - $style_string = Font_Metrics::get_type("{$style['weight']} {$style['style']}"); - - if ( !isset($entry[$style_string]) ) { - $entry[$style_string] = $cache_entry; - - // Download the remote file - file_put_contents($local_temp_file, file_get_contents($remote_file, null, $context)); - - $font = Font::load($local_temp_file); - - if (!$font) { - unlink($local_temp_file); - return false; - } - - $font->parse(); - $font->saveAdobeFontMetrics("$cache_entry.ufm"); - - unlink($local_temp_file); - - if ( !file_exists("$cache_entry.ufm") ) { - return false; - } - - // Save the changes - file_put_contents($local_file, file_get_contents($remote_file, null, $context)); - Font_Metrics::set_font_family($fontname, $entry); - Font_Metrics::save_font_families(); - } - - return true; - } -} - -Font_Metrics::load_font_families(); diff --git a/library/vendor/dompdf/include/frame.cls.php b/library/vendor/dompdf/include/frame.cls.php deleted file mode 100644 index bc2f26d40..000000000 --- a/library/vendor/dompdf/include/frame.cls.php +++ /dev/null @@ -1,1191 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * The main Frame class - * - * This class represents a single HTML element. This class stores - * positioning information as well as containing block location and - * dimensions. Style information for the element is stored in a {@link - * Style} object. Tree structure is maintained via the parent & children - * links. - * - * @access protected - * @package dompdf - */ -class Frame { - - /** - * The DOMElement or DOMText object this frame represents - * - * @var DOMElement|DOMText - */ - protected $_node; - - /** - * Unique identifier for this frame. Used to reference this frame - * via the node. - * - * @var string - */ - protected $_id; - - /** - * Unique id counter - */ - static /*protected*/ $ID_COUNTER = 0; - - /** - * This frame's calculated style - * - * @var Style - */ - protected $_style; - - /** - * This frame's original style. Needed for cases where frames are - * split across pages. - * - * @var Style - */ - protected $_original_style; - - /** - * This frame's parent in the document tree. - * - * @var Frame - */ - protected $_parent; - - /** - * This frame's children - * - * @var Frame[] - */ - protected $_frame_list; - - /** - * This frame's first child. All children are handled as a - * doubly-linked list. - * - * @var Frame - */ - protected $_first_child; - - /** - * This frame's last child. - * - * @var Frame - */ - protected $_last_child; - - /** - * This frame's previous sibling in the document tree. - * - * @var Frame - */ - protected $_prev_sibling; - - /** - * This frame's next sibling in the document tree. - * - * @var Frame - */ - protected $_next_sibling; - - /** - * This frame's containing block (used in layout): array(x, y, w, h) - * - * @var float[] - */ - protected $_containing_block; - - /** - * Position on the page of the top-left corner of the margin box of - * this frame: array(x,y) - * - * @var float[] - */ - protected $_position; - - /** - * Absolute opacity of this frame - * - * @var float - */ - protected $_opacity; - - /** - * This frame's decorator - * - * @var Frame_Decorator - */ - protected $_decorator; - - /** - * This frame's containing line box - * - * @var Line_Box - */ - protected $_containing_line; - - protected $_is_cache = array(); - - /** - * Tells wether the frame was already pushed to the next page - * - * @var bool - */ - public $_already_pushed = false; - - public $_float_next_line = false; - - /** - * Tells wether the frame was split - * - * @var bool - */ - public $_splitted; - - static $_ws_state = self::WS_SPACE; - - const WS_TEXT = 1; - const WS_SPACE = 2; - - /** - * Class destructor - */ - function __destruct() { - clear_object($this); - } - - /** - * Class constructor - * - * @param DOMNode $node the DOMNode this frame represents - */ - function __construct(DOMNode $node) { - $this->_node = $node; - - $this->_parent = null; - $this->_first_child = null; - $this->_last_child = null; - $this->_prev_sibling = $this->_next_sibling = null; - - $this->_style = null; - $this->_original_style = null; - - $this->_containing_block = array( - "x" => null, - "y" => null, - "w" => null, - "h" => null, - ); - - $this->_containing_block[0] =& $this->_containing_block["x"]; - $this->_containing_block[1] =& $this->_containing_block["y"]; - $this->_containing_block[2] =& $this->_containing_block["w"]; - $this->_containing_block[3] =& $this->_containing_block["h"]; - - $this->_position = array( - "x" => null, - "y" => null, - ); - - $this->_position[0] =& $this->_position["x"]; - $this->_position[1] =& $this->_position["y"]; - - $this->_opacity = 1.0; - $this->_decorator = null; - - $this->set_id( self::$ID_COUNTER++ ); - } - - // WIP : preprocessing to remove all the unused whitespace - protected function ws_trim(){ - if ( $this->ws_keep() ) { - return; - } - - switch(self::$_ws_state) { - case self::WS_SPACE: - $node = $this->_node; - - if ( $node->nodeName === "#text" ) { - $node->nodeValue = preg_replace("/[ \t\r\n\f]+/u", " ", $node->nodeValue); - - // starts with a whitespace - if ( isset($node->nodeValue[0]) && $node->nodeValue[0] === " " ) { - $node->nodeValue = ltrim($node->nodeValue); - } - - // if not empty - if ( $node->nodeValue !== "" ) { - // change the current state (text) - self::$_ws_state = self::WS_TEXT; - - // ends with a whitespace - if ( preg_match("/[ \t\r\n\f]+$/u", $node->nodeValue) ) { - $node->nodeValue = ltrim($node->nodeValue); - } - } - } - break; - - case self::WS_TEXT: - } - } - - protected function ws_keep(){ - $whitespace = $this->get_style()->white_space; - return in_array($whitespace, array("pre", "pre-wrap", "pre-line")); - } - - protected function ws_is_text(){ - $node = $this->get_node(); - - if ($node->nodeName === "img") { - return true; - } - - if ( !$this->is_in_flow() ) { - return false; - } - - if ($this->is_text_node()) { - return trim($node->nodeValue) !== ""; - } - - return true; - } - - /** - * "Destructor": forcibly free all references held by this frame - * - * @param bool $recursive if true, call dispose on all children - */ - function dispose($recursive = false) { - - if ( $recursive ) { - while ( $child = $this->_first_child ) { - $child->dispose(true); - } - } - - // Remove this frame from the tree - if ( $this->_prev_sibling ) { - $this->_prev_sibling->_next_sibling = $this->_next_sibling; - } - - if ( $this->_next_sibling ) { - $this->_next_sibling->_prev_sibling = $this->_prev_sibling; - } - - if ( $this->_parent && $this->_parent->_first_child === $this ) { - $this->_parent->_first_child = $this->_next_sibling; - } - - if ( $this->_parent && $this->_parent->_last_child === $this ) { - $this->_parent->_last_child = $this->_prev_sibling; - } - - if ( $this->_parent ) { - $this->_parent->get_node()->removeChild($this->_node); - } - - $this->_style->dispose(); - $this->_style = null; - unset($this->_style); - - $this->_original_style->dispose(); - $this->_original_style = null; - unset($this->_original_style); - - } - - // Re-initialize the frame - function reset() { - $this->_position["x"] = null; - $this->_position["y"] = null; - - $this->_containing_block["x"] = null; - $this->_containing_block["y"] = null; - $this->_containing_block["w"] = null; - $this->_containing_block["h"] = null; - - $this->_style = null; - unset($this->_style); - $this->_style = clone $this->_original_style; - } - - //........................................................................ - - /** - * @return DOMElement|DOMText - */ - function get_node() { - return $this->_node; - } - - /** - * @return string - */ - function get_id() { - return $this->_id; - } - - /** - * @return Style - */ - function get_style() { - return $this->_style; - } - - /** - * @return Style - */ - function get_original_style() { - return $this->_original_style; - } - - /** - * @return Frame - */ - function get_parent() { - return $this->_parent; - } - - /** - * @return Frame_Decorator - */ - function get_decorator() { - return $this->_decorator; - } - - /** - * @return Frame - */ - function get_first_child() { - return $this->_first_child; - } - - /** - * @return Frame - */ - function get_last_child() { - return $this->_last_child; - } - - /** - * @return Frame - */ - function get_prev_sibling() { - return $this->_prev_sibling; - } - - /** - * @return Frame - */ - function get_next_sibling() { - return $this->_next_sibling; - } - - /** - * @return FrameList|Frame[] - */ - function get_children() { - if ( isset($this->_frame_list) ) { - return $this->_frame_list; - } - - $this->_frame_list = new FrameList($this); - return $this->_frame_list; - } - - // Layout property accessors - - /** - * Containing block dimensions - * - * @param $i string The key of the wanted containing block's dimension (x, y, x, h) - * - * @return float[]|float - */ - function get_containing_block($i = null) { - if ( isset($i) ) { - return $this->_containing_block[$i]; - } - return $this->_containing_block; - } - - /** - * Block position - * - * @param $i string The key of the wanted position value (x, y) - * - * @return array|float - */ - function get_position($i = null) { - if ( isset($i) ) { - return $this->_position[$i]; - } - return $this->_position; - } - - //........................................................................ - - /** - * Return the height of the margin box of the frame, in pt. Meaningless - * unless the height has been calculated properly. - * - * @return float - */ - function get_margin_height() { - $style = $this->_style; - - return $style->length_in_pt(array( - $style->height, - $style->margin_top, - $style->margin_bottom, - $style->border_top_width, - $style->border_bottom_width, - $style->padding_top, - $style->padding_bottom - ), $this->_containing_block["h"]); - } - - /** - * Return the width of the margin box of the frame, in pt. Meaningless - * unless the width has been calculated properly. - * - * @return float - */ - function get_margin_width() { - $style = $this->_style; - - return $style->length_in_pt(array( - $style->width, - $style->margin_left, - $style->margin_right, - $style->border_left_width, - $style->border_right_width, - $style->padding_left, - $style->padding_right - ), $this->_containing_block["w"]); - } - - function get_break_margins(){ - $style = $this->_style; - - return $style->length_in_pt(array( - //$style->height, - $style->margin_top, - $style->margin_bottom, - $style->border_top_width, - $style->border_bottom_width, - $style->padding_top, - $style->padding_bottom - ), $this->_containing_block["h"]); - } - - /** - * Return the padding box (x,y,w,h) of the frame - * - * @return array - */ - function get_padding_box() { - $style = $this->_style; - $cb = $this->_containing_block; - - $x = $this->_position["x"] + - $style->length_in_pt(array($style->margin_left, - $style->border_left_width), - $cb["w"]); - - $y = $this->_position["y"] + - $style->length_in_pt(array($style->margin_top, - $style->border_top_width), - $cb["h"]); - - $w = $style->length_in_pt(array($style->padding_left, - $style->width, - $style->padding_right), - $cb["w"]); - - $h = $style->length_in_pt(array($style->padding_top, - $style->height, - $style->padding_bottom), - $cb["h"]); - - return array(0 => $x, "x" => $x, - 1 => $y, "y" => $y, - 2 => $w, "w" => $w, - 3 => $h, "h" => $h); - } - - /** - * Return the border box of the frame - * - * @return array - */ - function get_border_box() { - $style = $this->_style; - $cb = $this->_containing_block; - - $x = $this->_position["x"] + $style->length_in_pt($style->margin_left, $cb["w"]); - - $y = $this->_position["y"] + $style->length_in_pt($style->margin_top, $cb["h"]); - - $w = $style->length_in_pt(array($style->border_left_width, - $style->padding_left, - $style->width, - $style->padding_right, - $style->border_right_width), - $cb["w"]); - - $h = $style->length_in_pt(array($style->border_top_width, - $style->padding_top, - $style->height, - $style->padding_bottom, - $style->border_bottom_width), - $cb["h"]); - - return array(0 => $x, "x" => $x, - 1 => $y, "y" => $y, - 2 => $w, "w" => $w, - 3 => $h, "h" => $h); - } - - function get_opacity($opacity = null) { - if ( $opacity !== null ) { - $this->set_opacity($opacity); - } - return $this->_opacity; - } - - /** - * @return Line_Box - */ - function &get_containing_line() { - return $this->_containing_line; - } - - //........................................................................ - - // Set methods - function set_id($id) { - $this->_id = $id; - - // We can only set attributes of DOMElement objects (nodeType == 1). - // Since these are the only objects that we can assign CSS rules to, - // this shortcoming is okay. - if ( $this->_node->nodeType == XML_ELEMENT_NODE ) { - $this->_node->setAttribute("frame_id", $id); - } - } - - function set_style(Style $style) { - if ( is_null($this->_style) ) { - $this->_original_style = clone $style; - } - - //$style->set_frame($this); - $this->_style = $style; - } - - function set_decorator(Frame_Decorator $decorator) { - $this->_decorator = $decorator; - } - - function set_containing_block($x = null, $y = null, $w = null, $h = null) { - if ( is_array($x) ){ - foreach($x as $key => $val){ - $$key = $val; - } - } - - if (is_numeric($x)) { - $this->_containing_block["x"] = $x; - } - - if (is_numeric($y)) { - $this->_containing_block["y"] = $y; - } - - if (is_numeric($w)) { - $this->_containing_block["w"] = $w; - } - - if (is_numeric($h)) { - $this->_containing_block["h"] = $h; - } - } - - function set_position($x = null, $y = null) { - if ( is_array($x) ) { - list($x, $y) = array($x["x"], $x["y"]); - } - - if ( is_numeric($x) ) { - $this->_position["x"] = $x; - } - - if ( is_numeric($y) ) { - $this->_position["y"] = $y; - } - } - - function set_opacity($opacity) { - $parent = $this->get_parent(); - $base_opacity = (($parent && $parent->_opacity !== null) ? $parent->_opacity : 1.0); - $this->_opacity = $base_opacity * $opacity; - } - - function set_containing_line(Line_Box $line) { - $this->_containing_line = $line; - } - - //........................................................................ - - /** - * Tells if the frame is a text node - * @return bool - */ - function is_text_node() { - if ( isset($this->_is_cache["text_node"]) ) { - return $this->_is_cache["text_node"]; - } - - return $this->_is_cache["text_node"] = ($this->get_node()->nodeName === "#text"); - } - - function is_positionned() { - if ( isset($this->_is_cache["positionned"]) ) { - return $this->_is_cache["positionned"]; - } - - $position = $this->get_style()->position; - - return $this->_is_cache["positionned"] = in_array($position, Style::$POSITIONNED_TYPES); - } - - function is_absolute() { - if ( isset($this->_is_cache["absolute"]) ) { - return $this->_is_cache["absolute"]; - } - - $position = $this->get_style()->position; - - return $this->_is_cache["absolute"] = ($position === "absolute" || $position === "fixed"); - } - - function is_block() { - if ( isset($this->_is_cache["block"]) ) { - return $this->_is_cache["block"]; - } - - return $this->_is_cache["block"] = in_array($this->get_style()->display, Style::$BLOCK_TYPES); - } - - function is_in_flow() { - if ( isset($this->_is_cache["in_flow"]) ) { - return $this->_is_cache["in_flow"]; - } - - $enable_css_float = $this->get_style()->get_stylesheet()->get_dompdf()->get_option("enable_css_float"); - return $this->_is_cache["in_flow"] = !($enable_css_float && $this->get_style()->float !== "none" || $this->is_absolute()); - } - - function is_pre(){ - if ( isset($this->_is_cache["pre"]) ) { - return $this->_is_cache["pre"]; - } - - $white_space = $this->get_style()->white_space; - - return $this->_is_cache["pre"] = in_array($white_space, array("pre", "pre-wrap")); - } - - function is_table(){ - if ( isset($this->_is_cache["table"]) ) { - return $this->_is_cache["table"]; - } - - $display = $this->get_style()->display; - - return $this->_is_cache["table"] = in_array($display, Style::$TABLE_TYPES); - } - - - /** - * Inserts a new child at the beginning of the Frame - * - * @param $child Frame The new Frame to insert - * @param $update_node boolean Whether or not to update the DOM - */ - function prepend_child(Frame $child, $update_node = true) { - if ( $update_node ) { - $this->_node->insertBefore($child->_node, $this->_first_child ? $this->_first_child->_node : null); - } - - // Remove the child from its parent - if ( $child->_parent ) { - $child->_parent->remove_child($child, false); - } - - $child->_parent = $this; - $child->_prev_sibling = null; - - // Handle the first child - if ( !$this->_first_child ) { - $this->_first_child = $child; - $this->_last_child = $child; - $child->_next_sibling = null; - } - else { - $this->_first_child->_prev_sibling = $child; - $child->_next_sibling = $this->_first_child; - $this->_first_child = $child; - } - } - - /** - * Inserts a new child at the end of the Frame - * - * @param $child Frame The new Frame to insert - * @param $update_node boolean Whether or not to update the DOM - */ - function append_child(Frame $child, $update_node = true) { - if ( $update_node ) { - $this->_node->appendChild($child->_node); - } - - // Remove the child from its parent - if ( $child->_parent ) { - $child->_parent->remove_child($child, false); - } - - $child->_parent = $this; - $child->_next_sibling = null; - - // Handle the first child - if ( !$this->_last_child ) { - $this->_first_child = $child; - $this->_last_child = $child; - $child->_prev_sibling = null; - } - else { - $this->_last_child->_next_sibling = $child; - $child->_prev_sibling = $this->_last_child; - $this->_last_child = $child; - } - } - - /** - * Inserts a new child immediately before the specified frame - * - * @param $new_child Frame The new Frame to insert - * @param $ref Frame The Frame after the new Frame - * @param $update_node boolean Whether or not to update the DOM - * - * @throws DOMPDF_Exception - */ - function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) { - if ( $ref === $this->_first_child ) { - $this->prepend_child($new_child, $update_node); - return; - } - - if ( is_null($ref) ) { - $this->append_child($new_child, $update_node); - return; - } - - if ( $ref->_parent !== $this ) { - throw new DOMPDF_Exception("Reference child is not a child of this node."); - } - - // Update the node - if ( $update_node ) { - $this->_node->insertBefore($new_child->_node, $ref->_node); - } - - // Remove the child from its parent - if ( $new_child->_parent ) { - $new_child->_parent->remove_child($new_child, false); - } - - $new_child->_parent = $this; - $new_child->_next_sibling = $ref; - $new_child->_prev_sibling = $ref->_prev_sibling; - - if ( $ref->_prev_sibling ) { - $ref->_prev_sibling->_next_sibling = $new_child; - } - - $ref->_prev_sibling = $new_child; - } - - /** - * Inserts a new child immediately after the specified frame - * - * @param $new_child Frame The new Frame to insert - * @param $ref Frame The Frame before the new Frame - * @param $update_node boolean Whether or not to update the DOM - * - * @throws DOMPDF_Exception - */ - function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) { - if ( $ref === $this->_last_child ) { - $this->append_child($new_child, $update_node); - return; - } - - if ( is_null($ref) ) { - $this->prepend_child($new_child, $update_node); - return; - } - - if ( $ref->_parent !== $this ) { - throw new DOMPDF_Exception("Reference child is not a child of this node."); - } - - // Update the node - if ( $update_node ) { - if ( $ref->_next_sibling ) { - $next_node = $ref->_next_sibling->_node; - $this->_node->insertBefore($new_child->_node, $next_node); - } - else { - $new_child->_node = $this->_node->appendChild($new_child->_node); - } - } - - // Remove the child from its parent - if ( $new_child->_parent ) { - $new_child->_parent->remove_child($new_child, false); - } - - $new_child->_parent = $this; - $new_child->_prev_sibling = $ref; - $new_child->_next_sibling = $ref->_next_sibling; - - if ( $ref->_next_sibling ) { - $ref->_next_sibling->_prev_sibling = $new_child; - } - - $ref->_next_sibling = $new_child; - } - - - /** - * Remove a child frame - * - * @param Frame $child - * @param boolean $update_node Whether or not to remove the DOM node - * - * @throws DOMPDF_Exception - * @return Frame The removed child frame - */ - function remove_child(Frame $child, $update_node = true) { - if ( $child->_parent !== $this ) { - throw new DOMPDF_Exception("Child not found in this frame"); - } - - if ( $update_node ) { - $this->_node->removeChild($child->_node); - } - - if ( $child === $this->_first_child ) { - $this->_first_child = $child->_next_sibling; - } - - if ( $child === $this->_last_child ) { - $this->_last_child = $child->_prev_sibling; - } - - if ( $child->_prev_sibling ) { - $child->_prev_sibling->_next_sibling = $child->_next_sibling; - } - - if ( $child->_next_sibling ) { - $child->_next_sibling->_prev_sibling = $child->_prev_sibling; - } - - $child->_next_sibling = null; - $child->_prev_sibling = null; - $child->_parent = null; - return $child; - } - - //........................................................................ - - // Debugging function: - function __toString() { - // Skip empty text frames -// if ( $this->is_text_node() && -// preg_replace("/\s/", "", $this->_node->data) === "" ) -// return ""; - - - $str = "" . $this->_node->nodeName . ":
"; - //$str .= spl_object_hash($this->_node) . "
"; - $str .= "Id: " .$this->get_id() . "
"; - $str .= "Class: " .get_class($this) . "
"; - - if ( $this->is_text_node() ) { - $tmp = htmlspecialchars($this->_node->nodeValue); - $str .= "
'" .  mb_substr($tmp,0,70) .
-        (mb_strlen($tmp) > 70 ? "..." : "") . "'
"; - } - elseif ( $css_class = $this->_node->getAttribute("class") ) { - $str .= "CSS class: '$css_class'
"; - } - - if ( $this->_parent ) { - $str .= "\nParent:" . $this->_parent->_node->nodeName . - " (" . spl_object_hash($this->_parent->_node) . ") " . - "
"; - } - - if ( $this->_prev_sibling ) { - $str .= "Prev: " . $this->_prev_sibling->_node->nodeName . - " (" . spl_object_hash($this->_prev_sibling->_node) . ") " . - "
"; - } - - if ( $this->_next_sibling ) { - $str .= "Next: " . $this->_next_sibling->_node->nodeName . - " (" . spl_object_hash($this->_next_sibling->_node) . ") " . - "
"; - } - - $d = $this->get_decorator(); - while ($d && $d != $d->get_decorator()) { - $str .= "Decorator: " . get_class($d) . "
"; - $d = $d->get_decorator(); - } - - $str .= "Position: " . pre_r($this->_position, true); - $str .= "\nContaining block: " . pre_r($this->_containing_block, true); - $str .= "\nMargin width: " . pre_r($this->get_margin_width(), true); - $str .= "\nMargin height: " . pre_r($this->get_margin_height(), true); - - $str .= "\nStyle:
". $this->_style->__toString() . "
"; - - if ( $this->_decorator instanceof Block_Frame_Decorator ) { - $str .= "Lines:
";
-      foreach ($this->_decorator->get_line_boxes() as $line) {
-        foreach ($line->get_frames() as $frame) {
-          if ($frame instanceof Text_Frame_Decorator) {
-            $str .= "\ntext: ";
-            $str .= "'". htmlspecialchars($frame->get_text()) ."'";
-          }
-          else {
-            $str .= "\nBlock: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")";
-          }
-        }
-
-        $str .=
-          "\ny => " . $line->y . "\n" .
-          "w => " . $line->w . "\n" .
-          "h => " . $line->h . "\n" .
-          "left => " . $line->left . "\n" .
-          "right => " . $line->right . "\n";
-      }
-      $str .= "
"; - } - - $str .= "\n"; - if ( php_sapi_name() === "cli" ) { - $str = strip_tags(str_replace(array("
","",""), - array("\n","",""), - $str)); - } - - return $str; - } -} - -//------------------------------------------------------------------------ - -/** - * Linked-list IteratorAggregate - * - * @access private - * @package dompdf - */ -class FrameList implements IteratorAggregate { - protected $_frame; - - function __construct($frame) { $this->_frame = $frame; } - function getIterator() { return new FrameListIterator($this->_frame); } -} - -/** - * Linked-list Iterator - * - * Returns children in order and allows for list to change during iteration, - * provided the changes occur to or after the current element - * - * @access private - * @package dompdf - */ -class FrameListIterator implements Iterator { - - /** - * @var Frame - */ - protected $_parent; - - /** - * @var Frame - */ - protected $_cur; - - /** - * @var int - */ - protected $_num; - - function __construct(Frame $frame) { - $this->_parent = $frame; - $this->_cur = $frame->get_first_child(); - $this->_num = 0; - } - - function rewind() { - $this->_cur = $this->_parent->get_first_child(); - $this->_num = 0; - } - - /** - * @return bool - */ - function valid() { - return isset($this->_cur);// && ($this->_cur->get_prev_sibling() === $this->_prev); - } - - function key() { return $this->_num; } - - /** - * @return Frame - */ - function current() { return $this->_cur; } - - /** - * @return Frame - */ - function next() { - - $ret = $this->_cur; - if ( !$ret ) { - return null; - } - - $this->_cur = $this->_cur->get_next_sibling(); - $this->_num++; - return $ret; - } -} - -//------------------------------------------------------------------------ - -/** - * Pre-order IteratorAggregate - * - * @access private - * @package dompdf - */ -class FrameTreeList implements IteratorAggregate { - /** - * @var Frame - */ - protected $_root; - - function __construct(Frame $root) { $this->_root = $root; } - - /** - * @return FrameTreeIterator - */ - function getIterator() { return new FrameTreeIterator($this->_root); } -} - -/** - * Pre-order Iterator - * - * Returns frames in preorder traversal order (parent then children) - * - * @access private - * @package dompdf - */ -class FrameTreeIterator implements Iterator { - /** - * @var Frame - */ - protected $_root; - protected $_stack = array(); - - /** - * @var int - */ - protected $_num; - - function __construct(Frame $root) { - $this->_stack[] = $this->_root = $root; - $this->_num = 0; - } - - function rewind() { - $this->_stack = array($this->_root); - $this->_num = 0; - } - - /** - * @return bool - */ - function valid() { - return count($this->_stack) > 0; - } - - /** - * @return int - */ - function key() { - return $this->_num; - } - - /** - * @return Frame - */ - function current() { - return end($this->_stack); - } - - /** - * @return Frame - */ - function next() { - $b = end($this->_stack); - - // Pop last element - unset($this->_stack[ key($this->_stack) ]); - $this->_num++; - - // Push all children onto the stack in reverse order - if ( $c = $b->get_last_child() ) { - $this->_stack[] = $c; - while ( $c = $c->get_prev_sibling() ) { - $this->_stack[] = $c; - } - } - - return $b; - } -} - diff --git a/library/vendor/dompdf/include/frame_decorator.cls.php b/library/vendor/dompdf/include/frame_decorator.cls.php deleted file mode 100644 index 987e32ee1..000000000 --- a/library/vendor/dompdf/include/frame_decorator.cls.php +++ /dev/null @@ -1,717 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Base Frame_Decorator class - * - * @access private - * @package dompdf - */ -abstract class Frame_Decorator extends Frame { - const DEFAULT_COUNTER = "-dompdf-default-counter"; - - public $_counters = array(); // array([id] => counter_value) (for generated content) - - /** - * The root node of the DOM tree - * - * @var Frame - */ - protected $_root; - - /** - * The decorated frame - * - * @var Frame - */ - protected $_frame; - - /** - * Positioner object used to position this frame (Strategy pattern) - * - * @var Positioner - */ - protected $_positioner; - - /** - * Reflower object used to calculate frame dimensions (Strategy pattern) - * - * @var Frame_Reflower - */ - protected $_reflower; - - /** - * Reference to the current dompdf instance - * - * @var DOMPDF - */ - protected $_dompdf; - - /** - * First block parent - * - * @var Block_Frame_Decorator - */ - private $_block_parent; - - /** - * First positionned parent (position: relative | absolute | fixed) - * - * @var Frame_Decorator - */ - private $_positionned_parent; - - /** - * Class constructor - * - * @param Frame $frame The decoration target - * @param DOMPDF $dompdf The DOMPDF object - */ - function __construct(Frame $frame, DOMPDF $dompdf) { - $this->_frame = $frame; - $this->_root = null; - $this->_dompdf = $dompdf; - $frame->set_decorator($this); - } - - /** - * "Destructor": foribly free all references held by this object - * - * @param bool $recursive if true, call dispose on all children - */ - function dispose($recursive = false) { - if ( $recursive ) { - while ( $child = $this->get_first_child() ) { - $child->dispose(true); - } - } - - $this->_root = null; - unset($this->_root); - - $this->_frame->dispose(true); - $this->_frame = null; - unset($this->_frame); - - $this->_positioner = null; - unset($this->_positioner); - - $this->_reflower = null; - unset($this->_reflower); - } - - /** - * Return a copy of this frame with $node as its node - * - * @param DOMNode $node - * - * @return Frame - */ - function copy(DOMNode $node) { - $frame = new Frame($node); - $frame->set_style(clone $this->_frame->get_original_style()); - - return Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root); - } - - /** - * Create a deep copy: copy this node and all children - * - * @return Frame - */ - function deep_copy() { - $frame = new Frame($this->get_node()->cloneNode()); - $frame->set_style(clone $this->_frame->get_original_style()); - - $deco = Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root); - - foreach ($this->get_children() as $child) { - $deco->append_child($child->deep_copy()); - } - - return $deco; - } - - /** - * Delegate calls to decorated frame object - */ - function reset() { - $this->_frame->reset(); - - $this->_counters = array(); - - // Reset all children - foreach ($this->get_children() as $child) { - $child->reset(); - } - } - - // Getters ----------- - function get_id() { - return $this->_frame->get_id(); - } - - /** - * @return Frame - */ - function get_frame() { - return $this->_frame; - } - - /** - * @return DOMElement|DOMText - */ - function get_node() { - return $this->_frame->get_node(); - } - - /** - * @return Style - */ - function get_style() { - return $this->_frame->get_style(); - } - - /** - * @return Style - */ - function get_original_style() { - return $this->_frame->get_original_style(); - } - - /** - * @param integer $i - * - * @return array|float - */ - function get_containing_block($i = null) { - return $this->_frame->get_containing_block($i); - } - - /** - * @param integer $i - * - * @return array|float - */ - function get_position($i = null) { - return $this->_frame->get_position($i); - } - - /** - * @return DOMPDF - */ - function get_dompdf() { - return $this->_dompdf; - } - - /** - * @return float - */ - function get_margin_height() { - return $this->_frame->get_margin_height(); - } - - /** - * @return float - */ - function get_margin_width() { - return $this->_frame->get_margin_width(); - } - - /** - * @return array - */ - function get_padding_box() { - return $this->_frame->get_padding_box(); - } - - /** - * @return array - */ - function get_border_box() { - return $this->_frame->get_border_box(); - } - - /** - * @param integer $id - */ - function set_id($id) { - $this->_frame->set_id($id); - } - - /** - * @param Style $style - */ - function set_style(Style $style) { - $this->_frame->set_style($style); - } - - /** - * @param float $x - * @param float $y - * @param float $w - * @param float $h - */ - function set_containing_block($x = null, $y = null, $w = null, $h = null) { - $this->_frame->set_containing_block($x, $y, $w, $h); - } - - /** - * @param float $x - * @param float $y - */ - function set_position($x = null, $y = null) { - $this->_frame->set_position($x, $y); - } - - /** - * @return string - */ - function __toString() { - return $this->_frame->__toString(); - } - - /** - * @param Frame $child - * @param bool $update_node - */ - function prepend_child(Frame $child, $update_node = true) { - while ( $child instanceof Frame_Decorator ) { - $child = $child->_frame; - } - - $this->_frame->prepend_child($child, $update_node); - } - - /** - * @param Frame $child - * @param bool $update_node - */ - function append_child(Frame $child, $update_node = true) { - while ( $child instanceof Frame_Decorator ) { - $child = $child->_frame; - } - - $this->_frame->append_child($child, $update_node); - } - - /** - * @param Frame $new_child - * @param Frame $ref - * @param bool $update_node - */ - function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) { - while ( $new_child instanceof Frame_Decorator ) { - $new_child = $new_child->_frame; - } - - if ( $ref instanceof Frame_Decorator ) { - $ref = $ref->_frame; - } - - $this->_frame->insert_child_before($new_child, $ref, $update_node); - } - - /** - * @param Frame $new_child - * @param Frame $ref - * @param bool $update_node - */ - function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) { - while ( $new_child instanceof Frame_Decorator ) { - $new_child = $new_child->_frame; - } - - while ( $ref instanceof Frame_Decorator ) { - $ref = $ref->_frame; - } - - $this->_frame->insert_child_after($new_child, $ref, $update_node); - } - - /** - * @param Frame $child - * @param bool $update_node - * - * @return Frame - */ - function remove_child(Frame $child, $update_node = true) { - while ( $child instanceof Frame_Decorator ) { - $child = $child->_frame; - } - - return $this->_frame->remove_child($child, $update_node); - } - - /** - * @return Frame_Decorator - */ - function get_parent() { - $p = $this->_frame->get_parent(); - if ( $p && $deco = $p->get_decorator() ) { - while ( $tmp = $deco->get_decorator() ) { - $deco = $tmp; - } - - return $deco; - } - else if ( $p ) { - return $p; - } - - return null; - } - - /** - * @return Frame_Decorator - */ - function get_first_child() { - $c = $this->_frame->get_first_child(); - if ( $c && $deco = $c->get_decorator() ) { - while ( $tmp = $deco->get_decorator() ) { - $deco = $tmp; - } - - return $deco; - } - else if ( $c ) { - return $c; - } - - return null; - } - - /** - * @return Frame_Decorator - */ - function get_last_child() { - $c = $this->_frame->get_last_child(); - if ( $c && $deco = $c->get_decorator() ) { - while ( $tmp = $deco->get_decorator() ) { - $deco = $tmp; - } - - return $deco; - } - else if ( $c ) { - return $c; - } - - return null; - } - - /** - * @return Frame_Decorator - */ - function get_prev_sibling() { - $s = $this->_frame->get_prev_sibling(); - if ( $s && $deco = $s->get_decorator() ) { - while ( $tmp = $deco->get_decorator() ) { - $deco = $tmp; - } - return $deco; - } - else if ( $s ) { - return $s; - } - - return null; - } - - /** - * @return Frame_Decorator - */ - function get_next_sibling() { - $s = $this->_frame->get_next_sibling(); - if ( $s && $deco = $s->get_decorator() ) { - while ( $tmp = $deco->get_decorator() ) { - $deco = $tmp; - } - - return $deco; - } - else if ( $s ) { - return $s; - } - - return null; - } - - /** - * @return FrameTreeList - */ - function get_subtree() { - return new FrameTreeList($this); - } - - function set_positioner(Positioner $posn) { - $this->_positioner = $posn; - if ( $this->_frame instanceof Frame_Decorator ) { - $this->_frame->set_positioner($posn); - } - } - - function set_reflower(Frame_Reflower $reflower) { - $this->_reflower = $reflower; - if ( $this->_frame instanceof Frame_Decorator ) { - $this->_frame->set_reflower( $reflower ); - } - } - - /** - * @return Frame_Reflower - */ - function get_reflower() { - return $this->_reflower; - } - - /** - * @param Frame $root - */ - function set_root(Frame $root) { - $this->_root = $root; - - if ( $this->_frame instanceof Frame_Decorator ) { - $this->_frame->set_root($root); - } - } - - /** - * @return Page_Frame_Decorator - */ - function get_root() { - return $this->_root; - } - - /** - * @return Block_Frame_Decorator - */ - function find_block_parent() { - // Find our nearest block level parent - $p = $this->get_parent(); - - while ( $p ) { - if ( $p->is_block() ) { - break; - } - - $p = $p->get_parent(); - } - - return $this->_block_parent = $p; - } - - /** - * @return Frame_Decorator - */ - function find_positionned_parent() { - // Find our nearest relative positionned parent - $p = $this->get_parent(); - while ( $p ) { - if ( $p->is_positionned() ) { - break; - } - - $p = $p->get_parent(); - } - - if ( !$p ) { - $p = $this->_root->get_first_child(); // - } - - return $this->_positionned_parent = $p; - } - - /** - * split this frame at $child. - * The current frame is cloned and $child and all children following - * $child are added to the clone. The clone is then passed to the - * current frame's parent->split() method. - * - * @param Frame $child - * @param boolean $force_pagebreak - * - * @throws DOMPDF_Exception - * @return void - */ - function split(Frame $child = null, $force_pagebreak = false) { - // decrement any counters that were incremented on the current node, unless that node is the body - $style = $this->_frame->get_style(); - if ( $this->_frame->get_node()->nodeName !== "body" && $style->counter_increment && ($decrement = $style->counter_increment) !== "none" ) { - $this->decrement_counters($decrement); - } - - if ( is_null( $child ) ) { - // check for counter increment on :before content (always a child of the selected element @link Frame_Reflower::_set_content) - // this can push the current node to the next page before counter rules have bubbled up (but only if - // it's been rendered, thus the position check) - if ( !$this->is_text_node() && $this->get_node()->hasAttribute("dompdf_before_frame_id") ) { - foreach($this->_frame->get_children() as $child) { - if ( $this->get_node()->getAttribute("dompdf_before_frame_id") == $child->get_id() && $child->get_position('x') !== NULL ) { - $style = $child->get_style(); - if ( $style->counter_increment && ($decrement = $style->counter_increment) !== "none" ) { - $this->decrement_counters($decrement); - } - } - } - } - $this->get_parent()->split($this, $force_pagebreak); - return; - } - - if ( $child->get_parent() !== $this ) { - throw new DOMPDF_Exception("Unable to split: frame is not a child of this one."); - } - - $node = $this->_frame->get_node(); - - $split = $this->copy( $node->cloneNode() ); - $split->reset(); - $split->get_original_style()->text_indent = 0; - $split->_splitted = true; - - // The body's properties must be kept - if ( $node->nodeName !== "body" ) { - // Style reset on the first and second parts - $style = $this->_frame->get_style(); - $style->margin_bottom = 0; - $style->padding_bottom = 0; - $style->border_bottom = 0; - - // second - $orig_style = $split->get_original_style(); - $orig_style->text_indent = 0; - $orig_style->margin_top = 0; - $orig_style->padding_top = 0; - $orig_style->border_top = 0; - } - - $this->get_parent()->insert_child_after($split, $this); - - // Add $frame and all following siblings to the new split node - $iter = $child; - while ($iter) { - $frame = $iter; - $iter = $iter->get_next_sibling(); - $frame->reset(); - $split->append_child($frame); - } - - $this->get_parent()->split($split, $force_pagebreak); - - // If this node resets a counter save the current value to use when rendering on the next page - if ( $style->counter_reset && ( $reset = $style->counter_reset ) !== "none" ) { - $vars = preg_split( '/\s+/' , trim( $reset ) , 2 ); - $split->_counters[ '__' . $vars[0] ] = $this->lookup_counter_frame( $vars[0] )->_counters[$vars[0]]; - } - } - - function reset_counter($id = self::DEFAULT_COUNTER, $value = 0) { - $this->get_parent()->_counters[$id] = intval($value); - } - - function decrement_counters($counters) { - foreach($counters as $id => $increment) { - $this->increment_counter($id, intval($increment) * -1); - } - } - - function increment_counters($counters) { - foreach($counters as $id => $increment) { - $this->increment_counter($id, intval($increment)); - } - } - - function increment_counter($id = self::DEFAULT_COUNTER, $increment = 1) { - $counter_frame = $this->lookup_counter_frame($id); - - if ( $counter_frame ) { - if ( !isset($counter_frame->_counters[$id]) ) { - $counter_frame->_counters[$id] = 0; - } - - $counter_frame->_counters[$id] += $increment; - } - } - - function lookup_counter_frame($id = self::DEFAULT_COUNTER) { - $f = $this->get_parent(); - - while( $f ) { - if( isset($f->_counters[$id]) ) { - return $f; - } - $fp = $f->get_parent(); - - if ( !$fp ) { - return $f; - } - - $f = $fp; - } - } - - // TODO: What version is the best : this one or the one in List_Bullet_Renderer ? - function counter_value($id = self::DEFAULT_COUNTER, $type = "decimal") { - $type = mb_strtolower($type); - - if ( !isset($this->_counters[$id]) ) { - $this->_counters[$id] = 0; - } - - $value = $this->_counters[$id]; - - switch ($type) { - default: - case "decimal": - return $value; - - case "decimal-leading-zero": - return str_pad($value, 2, "0"); - - case "lower-roman": - return dec2roman($value); - - case "upper-roman": - return mb_strtoupper(dec2roman($value)); - - case "lower-latin": - case "lower-alpha": - return chr( ($value % 26) + ord('a') - 1); - - case "upper-latin": - case "upper-alpha": - return chr( ($value % 26) + ord('A') - 1); - - case "lower-greek": - return unichr($value + 944); - - case "upper-greek": - return unichr($value + 912); - } - } - - final function position() { - $this->_positioner->position(); - } - - final function move($offset_x, $offset_y, $ignore_self = false) { - $this->_positioner->move($offset_x, $offset_y, $ignore_self); - } - - final function reflow(Block_Frame_Decorator $block = null) { - // Uncomment this to see the frames before they're laid out, instead of - // during rendering. - //echo $this->_frame; flush(); - $this->_reflower->reflow($block); - } - - final function get_min_max_width() { - return $this->_reflower->get_min_max_width(); - } -} diff --git a/library/vendor/dompdf/include/frame_factory.cls.php b/library/vendor/dompdf/include/frame_factory.cls.php deleted file mode 100644 index 70813d2e3..000000000 --- a/library/vendor/dompdf/include/frame_factory.cls.php +++ /dev/null @@ -1,252 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Contains frame decorating logic - * - * This class is responsible for assigning the correct {@link Frame_Decorator}, - * {@link Positioner}, and {@link Frame_Reflower} objects to {@link Frame} - * objects. This is determined primarily by the Frame's display type, but - * also by the Frame's node's type (e.g. DomElement vs. #text) - * - * @access private - * @package dompdf - */ -class Frame_Factory { - - /** - * Decorate the root Frame - * - * @param $root Frame The frame to decorate - * @param $dompdf DOMPDF The dompdf instance - * @return Page_Frame_Decorator - */ - static function decorate_root(Frame $root, DOMPDF $dompdf) { - $frame = new Page_Frame_Decorator($root, $dompdf); - $frame->set_reflower( new Page_Frame_Reflower($frame) ); - $root->set_decorator($frame); - return $frame; - } - - /** - * Decorate a Frame - * - * @param Frame $frame The frame to decorate - * @param DOMPDF $dompdf The dompdf instance - * @param Frame $root The frame to decorate - * - * @throws DOMPDF_Exception - * @return Frame_Decorator - * FIXME: this is admittedly a little smelly... - */ - static function decorate_frame(Frame $frame, DOMPDF $dompdf, Frame $root = null) { - if ( is_null($dompdf) ) { - throw new DOMPDF_Exception("The DOMPDF argument is required"); - } - - $style = $frame->get_style(); - - // Floating (and more generally out-of-flow) elements are blocks - // http://coding.smashingmagazine.com/2007/05/01/css-float-theory-things-you-should-know/ - if ( !$frame->is_in_flow() && in_array($style->display, Style::$INLINE_TYPES)) { - $style->display = "block"; - } - - $display = $style->display; - - switch ($display) { - - case "block": - $positioner = "Block"; - $decorator = "Block"; - $reflower = "Block"; - break; - - case "inline-block": - $positioner = "Inline"; - $decorator = "Block"; - $reflower = "Block"; - break; - - case "inline": - $positioner = "Inline"; - if ( $frame->is_text_node() ) { - $decorator = "Text"; - $reflower = "Text"; - } - else { - $enable_css_float = $dompdf->get_option("enable_css_float"); - if ( $enable_css_float && $style->float !== "none" ) { - $decorator = "Block"; - $reflower = "Block"; - } - else { - $decorator = "Inline"; - $reflower = "Inline"; - } - } - break; - - case "table": - $positioner = "Block"; - $decorator = "Table"; - $reflower = "Table"; - break; - - case "inline-table": - $positioner = "Inline"; - $decorator = "Table"; - $reflower = "Table"; - break; - - case "table-row-group": - case "table-header-group": - case "table-footer-group": - $positioner = "Null"; - $decorator = "Table_Row_Group"; - $reflower = "Table_Row_Group"; - break; - - case "table-row": - $positioner = "Null"; - $decorator = "Table_Row"; - $reflower = "Table_Row"; - break; - - case "table-cell": - $positioner = "Table_Cell"; - $decorator = "Table_Cell"; - $reflower = "Table_Cell"; - break; - - case "list-item": - $positioner = "Block"; - $decorator = "Block"; - $reflower = "Block"; - break; - - case "-dompdf-list-bullet": - if ( $style->list_style_position === "inside" ) { - $positioner = "Inline"; - } - else { - $positioner = "List_Bullet"; - } - - if ( $style->list_style_image !== "none" ) { - $decorator = "List_Bullet_Image"; - } - else { - $decorator = "List_Bullet"; - } - - $reflower = "List_Bullet"; - break; - - case "-dompdf-image": - $positioner = "Inline"; - $decorator = "Image"; - $reflower = "Image"; - break; - - case "-dompdf-br": - $positioner = "Inline"; - $decorator = "Inline"; - $reflower = "Inline"; - break; - - default: - // FIXME: should throw some sort of warning or something? - case "none": - if ( $style->_dompdf_keep !== "yes" ) { - // Remove the node and the frame - $frame->get_parent()->remove_child($frame); - return; - } - - $positioner = "Null"; - $decorator = "Null"; - $reflower = "Null"; - break; - } - - // Handle CSS position - $position = $style->position; - - if ( $position === "absolute" ) { - $positioner = "Absolute"; - } - else if ( $position === "fixed" ) { - $positioner = "Fixed"; - } - - $node = $frame->get_node(); - - // Handle nodeName - if ( $node->nodeName === "img" ) { - $style->display = "-dompdf-image"; - $decorator = "Image"; - $reflower = "Image"; - } - - $positioner .= "_Positioner"; - $decorator .= "_Frame_Decorator"; - $reflower .= "_Frame_Reflower"; - - $deco = new $decorator($frame, $dompdf); - - $deco->set_positioner( new $positioner($deco) ); - $deco->set_reflower( new $reflower($deco) ); - - if ( $root ) { - $deco->set_root($root); - } - - if ( $display === "list-item" ) { - // Insert a list-bullet frame - $xml = $dompdf->get_dom(); - $bullet_node = $xml->createElement("bullet"); // arbitrary choice - $b_f = new Frame($bullet_node); - - $node = $frame->get_node(); - $parent_node = $node->parentNode; - - if ( $parent_node ) { - if ( !$parent_node->hasAttribute("dompdf-children-count") ) { - $xpath = new DOMXPath($xml); - $count = $xpath->query("li", $parent_node)->length; - $parent_node->setAttribute("dompdf-children-count", $count); - } - - if ( is_numeric($node->getAttribute("value")) ) { - $index = intval($node->getAttribute("value")); - } - else { - if ( !$parent_node->hasAttribute("dompdf-counter") ) { - $index = ($parent_node->hasAttribute("start") ? $parent_node->getAttribute("start") : 1); - } - else { - $index = $parent_node->getAttribute("dompdf-counter")+1; - } - } - - $parent_node->setAttribute("dompdf-counter", $index); - $bullet_node->setAttribute("dompdf-counter", $index); - } - - $new_style = $dompdf->get_css()->create_style(); - $new_style->display = "-dompdf-list-bullet"; - $new_style->inherit($style); - $b_f->set_style($new_style); - - $deco->prepend_child( Frame_Factory::decorate_frame($b_f, $dompdf, $root) ); - } - - return $deco; - } -} diff --git a/library/vendor/dompdf/include/frame_reflower.cls.php b/library/vendor/dompdf/include/frame_reflower.cls.php deleted file mode 100644 index 576039e1f..000000000 --- a/library/vendor/dompdf/include/frame_reflower.cls.php +++ /dev/null @@ -1,458 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Base reflower class - * - * Reflower objects are responsible for determining the width and height of - * individual frames. They also create line and page breaks as necessary. - * - * @access private - * @package dompdf - */ -abstract class Frame_Reflower { - - /** - * Frame for this reflower - * - * @var Frame - */ - protected $_frame; - - /** - * Cached min/max size - * - * @var array - */ - protected $_min_max_cache; - - function __construct(Frame $frame) { - $this->_frame = $frame; - $this->_min_max_cache = null; - } - - function dispose() { - clear_object($this); - } - - /** - * @return DOMPDF - */ - function get_dompdf() { - return $this->_frame->get_dompdf(); - } - - /** - * Collapse frames margins - * http://www.w3.org/TR/CSS2/box.html#collapsing-margins - */ - protected function _collapse_margins() { - $frame = $this->_frame; - $cb = $frame->get_containing_block(); - $style = $frame->get_style(); - - if ( !$frame->is_in_flow() ) { - return; - } - - $t = $style->length_in_pt($style->margin_top, $cb["h"]); - $b = $style->length_in_pt($style->margin_bottom, $cb["h"]); - - // Handle 'auto' values - if ( $t === "auto" ) { - $style->margin_top = "0pt"; - $t = 0; - } - - if ( $b === "auto" ) { - $style->margin_bottom = "0pt"; - $b = 0; - } - - // Collapse vertical margins: - $n = $frame->get_next_sibling(); - if ( $n && !$n->is_block() ) { - while ( $n = $n->get_next_sibling() ) { - if ( $n->is_block() ) { - break; - } - - if ( !$n->get_first_child() ) { - $n = null; - break; - } - } - } - - if ( $n ) { - $n_style = $n->get_style(); - $b = max($b, $n_style->length_in_pt($n_style->margin_top, $cb["h"])); - $n_style->margin_top = "0pt"; - $style->margin_bottom = $b."pt"; - } - - // Collapse our first child's margin - /*$f = $this->_frame->get_first_child(); - if ( $f && !$f->is_block() ) { - while ( $f = $f->get_next_sibling() ) { - if ( $f->is_block() ) { - break; - } - - if ( !$f->get_first_child() ) { - $f = null; - break; - } - } - } - - // Margin are collapsed only between block elements - if ( $f ) { - $f_style = $f->get_style(); - $t = max($t, $f_style->length_in_pt($f_style->margin_top, $cb["h"])); - $style->margin_top = $t."pt"; - $f_style->margin_bottom = "0pt"; - }*/ - } - - //........................................................................ - - abstract function reflow(Block_Frame_Decorator $block = null); - - //........................................................................ - - // Required for table layout: Returns an array(0 => min, 1 => max, "min" - // => min, "max" => max) of the minimum and maximum widths of this frame. - // This provides a basic implementation. Child classes should override - // this if necessary. - function get_min_max_width() { - if ( !is_null($this->_min_max_cache) ) { - return $this->_min_max_cache; - } - - $style = $this->_frame->get_style(); - - // Account for margins & padding - $dims = array($style->padding_left, - $style->padding_right, - $style->border_left_width, - $style->border_right_width, - $style->margin_left, - $style->margin_right); - - $cb_w = $this->_frame->get_containing_block("w"); - $delta = $style->length_in_pt($dims, $cb_w); - - // Handle degenerate case - if ( !$this->_frame->get_first_child() ) { - return $this->_min_max_cache = array( - $delta, $delta, - "min" => $delta, - "max" => $delta, - ); - } - - $low = array(); - $high = array(); - - for ( $iter = $this->_frame->get_children()->getIterator(); - $iter->valid(); - $iter->next() ) { - - $inline_min = 0; - $inline_max = 0; - - // Add all adjacent inline widths together to calculate max width - while ( $iter->valid() && in_array( $iter->current()->get_style()->display, Style::$INLINE_TYPES ) ) { - - $child = $iter->current(); - - $minmax = $child->get_min_max_width(); - - if ( in_array( $iter->current()->get_style()->white_space, array("pre", "nowrap") ) ) { - $inline_min += $minmax["min"]; - } - else { - $low[] = $minmax["min"]; - } - - $inline_max += $minmax["max"]; - $iter->next(); - - } - - if ( $inline_max > 0 ) $high[] = $inline_max; - if ( $inline_min > 0 ) $low[] = $inline_min; - - if ( $iter->valid() ) { - list($low[], $high[]) = $iter->current()->get_min_max_width(); - continue; - } - - } - $min = count($low) ? max($low) : 0; - $max = count($high) ? max($high) : 0; - - // Use specified width if it is greater than the minimum defined by the - // content. If the width is a percentage ignore it for now. - $width = $style->width; - if ( $width !== "auto" && !is_percent($width) ) { - $width = $style->length_in_pt($width, $cb_w); - if ( $min < $width ) $min = $width; - if ( $max < $width ) $max = $width; - } - - $min += $delta; - $max += $delta; - return $this->_min_max_cache = array($min, $max, "min"=>$min, "max"=>$max); - } - - /** - * Parses a CSS string containing quotes and escaped hex characters - * - * @param $string string The CSS string to parse - * @param $single_trim - * @return string - */ - protected function _parse_string($string, $single_trim = false) { - if ( $single_trim ) { - $string = preg_replace('/^[\"\']/', "", $string); - $string = preg_replace('/[\"\']$/', "", $string); - } - else { - $string = trim($string, "'\""); - } - - $string = str_replace(array("\\\n",'\\"',"\\'"), - array("",'"',"'"), $string); - - // Convert escaped hex characters into ascii characters (e.g. \A => newline) - $string = preg_replace_callback("/\\\\([0-9a-fA-F]{0,6})/", - create_function('$matches', - 'return unichr(hexdec($matches[1]));'), - $string); - return $string; - } - - /** - * Parses a CSS "quotes" property - * - * @return array|null An array of pairs of quotes - */ - protected function _parse_quotes() { - - // Matches quote types - $re = '/(\'[^\']*\')|(\"[^\"]*\")/'; - - $quotes = $this->_frame->get_style()->quotes; - - // split on spaces, except within quotes - if ( !preg_match_all($re, "$quotes", $matches, PREG_SET_ORDER) ) { - return null; - } - - $quotes_array = array(); - foreach($matches as &$_quote){ - $quotes_array[] = $this->_parse_string($_quote[0], true); - } - - if ( empty($quotes_array) ) { - $quotes_array = array('"', '"'); - } - - return array_chunk($quotes_array, 2); - } - - /** - * Parses the CSS "content" property - * - * @return string|null The resulting string - */ - protected function _parse_content() { - - // Matches generated content - $re = "/\n". - "\s(counters?\\([^)]*\\))|\n". - "\A(counters?\\([^)]*\\))|\n". - "\s([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?_frame->get_style()->content; - - $quotes = $this->_parse_quotes(); - - // split on spaces, except within quotes - if ( !preg_match_all($re, $content, $matches, PREG_SET_ORDER) ) { - return null; - } - - $text = ""; - - foreach ($matches as $match) { - - if ( isset($match[2]) && $match[2] !== "" ) { - $match[1] = $match[2]; - } - - if ( isset($match[6]) && $match[6] !== "" ) { - $match[4] = $match[6]; - } - - if ( isset($match[8]) && $match[8] !== "" ) { - $match[7] = $match[8]; - } - - if ( isset($match[1]) && $match[1] !== "" ) { - - // counters?(...) - $match[1] = mb_strtolower(trim($match[1])); - - // Handle counter() references: - // http://www.w3.org/TR/CSS21/generate.html#content - - $i = mb_strpos($match[1], ")"); - if ( $i === false ) { - continue; - } - - preg_match( '/(counters?)(^\()*?\(\s*([^\s,]+)\s*(,\s*["\']?([^"\'\)]+)["\']?\s*(,\s*([^\s)]+)\s*)?)?\)/i' , $match[1] , $args ); - $counter_id = $args[3]; - if ( strtolower( $args[1] ) == 'counter' ) { - // counter(name [,style]) - if ( isset( $args[5] ) ) { - $type = trim( $args[5] ); - } - else { - $type = null; - } - $p = $this->_frame->lookup_counter_frame( $counter_id ); - - $text .= $p->counter_value($counter_id, $type); - - } - else if ( strtolower( $args[1] ) == 'counters' ) { - // counters(name, string [,style]) - if ( isset($args[5]) ) { - $string = $this->_parse_string( $args[5] ); - } - else { - $string = ""; - } - - if ( isset( $args[7] ) ) { - $type = trim( $args[7] ); - } - else { - $type = null; - } - - $p = $this->_frame->lookup_counter_frame($counter_id); - $tmp = array(); - while ($p) { - // We only want to use the counter values when they actually increment the counter - if ( array_key_exists( $counter_id , $p->_counters ) ) { - array_unshift( $tmp , $p->counter_value($counter_id, $type) ); - } - $p = $p->lookup_counter_frame($counter_id); - - } - $text .= implode( $string , $tmp ); - - } - else { - // countertops? - continue; - } - - } - else if ( isset($match[4]) && $match[4] !== "" ) { - // String match - $text .= $this->_parse_string($match[4]); - } - else if ( isset($match[7]) && $match[7] !== "" ) { - // Directive match - - if ( $match[7] === "open-quote" ) { - // FIXME: do something here - $text .= $quotes[0][0]; - } - else if ( $match[7] === "close-quote" ) { - // FIXME: do something else here - $text .= $quotes[0][1]; - } - else if ( $match[7] === "no-open-quote" ) { - // FIXME: - } - else if ( $match[7] === "no-close-quote" ) { - // FIXME: - } - else if ( mb_strpos($match[7],"attr(") === 0 ) { - - $i = mb_strpos($match[7],")"); - if ( $i === false ) { - continue; - } - - $attr = mb_substr($match[7], 5, $i - 5); - if ( $attr == "" ) { - continue; - } - - $text .= $this->_frame->get_parent()->get_node()->getAttribute($attr); - } - else { - continue; - } - } - } - - return $text; - } - - /** - * Sets the generated content of a generated frame - */ - protected function _set_content(){ - $frame = $this->_frame; - $style = $frame->get_style(); - - // if the element was pushed to a new page use the saved counter value, otherwise use the CSS reset value - if ( $style->counter_reset && ($reset = $style->counter_reset) !== "none" ) { - $vars = preg_split('/\s+/', trim($reset), 2); - $frame->reset_counter( $vars[0] , ( isset($frame->_counters['__'.$vars[0]]) ? $frame->_counters['__'.$vars[0]] : ( isset($vars[1]) ? $vars[1] : 0 ) ) ); - } - - if ( $style->counter_increment && ($increment = $style->counter_increment) !== "none" ) { - $frame->increment_counters($increment); - } - - if ( $style->content && !$frame->get_first_child() && $frame->get_node()->nodeName === "dompdf_generated" ) { - $content = $this->_parse_content(); - // add generated content to the font subset - // FIXME: This is currently too late because the font subset has already been generated. - // See notes in issue #750. - if ( $frame->get_dompdf()->get_option("enable_font_subsetting") && $frame->get_dompdf()->get_canvas() instanceof CPDF_Adapter ) { - $frame->get_dompdf()->get_canvas()->register_string_subset($style->font_family, $content); - } - - $node = $frame->get_node()->ownerDocument->createTextNode($content); - - $new_style = $style->get_stylesheet()->create_style(); - $new_style->inherit($style); - - $new_frame = new Frame($node); - $new_frame->set_style($new_style); - - Frame_Factory::decorate_frame($new_frame, $frame->get_dompdf(), $frame->get_root()); - $frame->append_child($new_frame); - } - } -} diff --git a/library/vendor/dompdf/include/frame_tree.cls.php b/library/vendor/dompdf/include/frame_tree.cls.php deleted file mode 100644 index 257ca097e..000000000 --- a/library/vendor/dompdf/include/frame_tree.cls.php +++ /dev/null @@ -1,241 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Represents an entire document as a tree of frames - * - * The Frame_Tree consists of {@link Frame} objects each tied to specific - * DOMNode objects in a specific DomDocument. The Frame_Tree has the same - * structure as the DomDocument, but adds additional capabalities for - * styling and layout. - * - * @package dompdf - * @access protected - */ -class Frame_Tree { - - /** - * Tags to ignore while parsing the tree - * - * @var array - */ - static protected $_HIDDEN_TAGS = array("area", "base", "basefont", "head", "style", - "meta", "title", "colgroup", - "noembed", "noscript", "param", "#comment"); - /** - * The main DomDocument - * - * @see http://ca2.php.net/manual/en/ref.dom.php - * @var DomDocument - */ - protected $_dom; - - /** - * The root node of the FrameTree. - * - * @var Frame - */ - protected $_root; - - /** - * Subtrees of absolutely positioned elements - * - * @var array of Frames - */ - protected $_absolute_frames; - - /** - * A mapping of {@link Frame} objects to DOMNode objects - * - * @var array - */ - protected $_registry; - - - /** - * Class constructor - * - * @param DomDocument $dom the main DomDocument object representing the current html document - */ - function __construct(DomDocument $dom) { - $this->_dom = $dom; - $this->_root = null; - $this->_registry = array(); - } - - function __destruct() { - clear_object($this); - } - - /** - * Returns the DomDocument object representing the curent html document - * - * @return DOMDocument - */ - function get_dom() { - return $this->_dom; - } - - /** - * Returns the root frame of the tree - * - * @return Page_Frame_Decorator - */ - function get_root() { - return $this->_root; - } - - /** - * Returns a specific frame given its id - * - * @param string $id - * @return Frame - */ - function get_frame($id) { - return isset($this->_registry[$id]) ? $this->_registry[$id] : null; - } - - /** - * Returns a post-order iterator for all frames in the tree - * - * @return FrameTreeList|Frame[] - */ - function get_frames() { - return new FrameTreeList($this->_root); - } - - /** - * Builds the tree - */ - function build_tree() { - $html = $this->_dom->getElementsByTagName("html")->item(0); - if ( is_null($html) ) { - $html = $this->_dom->firstChild; - } - - if ( is_null($html) ) { - throw new DOMPDF_Exception("Requested HTML document contains no data."); - } - - $this->fix_tables(); - - $this->_root = $this->_build_tree_r($html); - } - - /** - * Adds missing TBODYs around TR - */ - protected function fix_tables(){ - $xp = new DOMXPath($this->_dom); - - // Move table caption before the table - // FIXME find a better way to deal with it... - $captions = $xp->query("//table/caption"); - foreach($captions as $caption) { - $table = $caption->parentNode; - $table->parentNode->insertBefore($caption, $table); - } - - $rows = $xp->query("//table/tr"); - foreach($rows as $row) { - $tbody = $this->_dom->createElement("tbody"); - $tbody = $row->parentNode->insertBefore($tbody, $row); - $tbody->appendChild($row); - } - } - - /** - * Recursively adds {@link Frame} objects to the tree - * - * Recursively build a tree of Frame objects based on a dom tree. - * No layout information is calculated at this time, although the - * tree may be adjusted (i.e. nodes and frames for generated content - * and images may be created). - * - * @param DOMNode $node the current DOMNode being considered - * @return Frame - */ - protected function _build_tree_r(DOMNode $node) { - - $frame = new Frame($node); - $id = $frame->get_id(); - $this->_registry[ $id ] = $frame; - - if ( !$node->hasChildNodes() ) { - return $frame; - } - - // Fixes 'cannot access undefined property for object with - // overloaded access', fix by Stefan radulian - // - //foreach ($node->childNodes as $child) { - - // Store the children in an array so that the tree can be modified - $children = array(); - for ($i = 0; $i < $node->childNodes->length; $i++) { - $children[] = $node->childNodes->item($i); - } - - foreach ($children as $child) { - $node_name = mb_strtolower($child->nodeName); - - // Skip non-displaying nodes - if ( in_array($node_name, self::$_HIDDEN_TAGS) ) { - if ( $node_name !== "head" && $node_name !== "style" ) { - $child->parentNode->removeChild($child); - } - - continue; - } - - // Skip empty text nodes - if ( $node_name === "#text" && $child->nodeValue == "" ) { - $child->parentNode->removeChild($child); - continue; - } - - // Skip empty image nodes - if ( $node_name === "img" && $child->getAttribute("src") == "" ) { - $child->parentNode->removeChild($child); - continue; - } - - $frame->append_child($this->_build_tree_r($child), false); - } - - return $frame; - } - - public function insert_node(DOMNode $node, DOMNode $new_node, $pos) { - if ( $pos === "after" || !$node->firstChild ) { - $node->appendChild($new_node); - } - else { - $node->insertBefore($new_node, $node->firstChild); - } - - $this->_build_tree_r($new_node); - - $frame_id = $new_node->getAttribute("frame_id"); - $frame = $this->get_frame($frame_id); - - $parent_id = $node->getAttribute("frame_id"); - $parent = $this->get_frame($parent_id); - - if ( $parent ) { - if ( $pos === "before" ) { - $parent->prepend_child($frame, false); - } - else { - $parent->append_child($frame, false); - } - } - - return $frame_id; - } -} diff --git a/library/vendor/dompdf/include/functions.inc.php b/library/vendor/dompdf/include/functions.inc.php deleted file mode 100644 index 265244aaf..000000000 --- a/library/vendor/dompdf/include/functions.inc.php +++ /dev/null @@ -1,1036 +0,0 @@ - - * @author Helmut Tischer - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -if ( !defined('PHP_VERSION_ID') ) { - $version = explode('.', PHP_VERSION); - define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2])); -} - -/** - * Defined a constant if not already defined - * - * @param string $name The constant name - * @param mixed $value The value - */ -function def($name, $value = true) { - if ( !defined($name) ) { - define($name, $value); - } -} - -if ( !function_exists("pre_r") ) { -/** - * print_r wrapper for html/cli output - * - * Wraps print_r() output in < pre > tags if the current sapi is not 'cli'. - * Returns the output string instead of displaying it if $return is true. - * - * @param mixed $mixed variable or expression to display - * @param bool $return - * - * @return string - */ -function pre_r($mixed, $return = false) { - if ( $return ) { - return "
" . print_r($mixed, true) . "
"; - } - - if ( php_sapi_name() !== "cli" ) { - echo "
";
-  }
-  
-  print_r($mixed);
-
-  if ( php_sapi_name() !== "cli" ) {
-    echo "
"; - } - else { - echo "\n"; - } - - flush(); - -} -} - -if ( !function_exists("pre_var_dump") ) { -/** - * var_dump wrapper for html/cli output - * - * Wraps var_dump() output in < pre > tags if the current sapi is not 'cli'. - * - * @param mixed $mixed variable or expression to display. - */ -function pre_var_dump($mixed) { - if ( php_sapi_name() !== "cli" ) { - echo "
";
-  }
-    
-  var_dump($mixed);
-  
-  if ( php_sapi_name() !== "cli" ) {
-    echo "
"; - } -} -} - -if ( !function_exists("d") ) { -/** - * generic debug function - * - * Takes everything and does its best to give a good debug output - * - * @param mixed $mixed variable or expression to display. - */ -function d($mixed) { - if ( php_sapi_name() !== "cli" ) { - echo "
";
-  }
-    
-  // line
-  if ( $mixed instanceof Line_Box ) {
-    echo $mixed;
-  }
-  
-  // other
-  else {
-    var_export($mixed);
-  }
-  
-  if ( php_sapi_name() !== "cli" ) {
-    echo "
"; - } -} -} - -/** - * builds a full url given a protocol, hostname, base path and url - * - * @param string $protocol - * @param string $host - * @param string $base_path - * @param string $url - * @return string - * - * Initially the trailing slash of $base_path was optional, and conditionally appended. - * However on dynamically created sites, where the page is given as url parameter, - * the base path might not end with an url. - * Therefore do not append a slash, and **require** the $base_url to ending in a slash - * when needed. - * Vice versa, on using the local file system path of a file, make sure that the slash - * is appended (o.k. also for Windows) - */ -function build_url($protocol, $host, $base_path, $url) { - $protocol = mb_strtolower($protocol); - if (strlen($url) == 0) { - //return $protocol . $host . rtrim($base_path, "/\\") . "/"; - return $protocol . $host . $base_path; - } - // Is the url already fully qualified or a Data URI? - if (mb_strpos($url, "://") !== false || mb_strpos($url, "data:") === 0) { - return $url; - } - $ret = $protocol; - if (!in_array(mb_strtolower($protocol), array("http://", "https://", "ftp://", "ftps://"))) { - //On Windows local file, an abs path can begin also with a '\' or a drive letter and colon - //drive: followed by a relative path would be a drive specific default folder. - //not known in php app code, treat as abs path - //($url[1] !== ':' || ($url[2]!=='\\' && $url[2]!=='/')) - if ($url[0] !== '/' && (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' || ($url[0] !== '\\' && $url[1] !== ':'))) { - // For rel path and local acess we ignore the host, and run the path through realpath() - $ret .= realpath($base_path) . '/'; - } - $ret .= $url; - $ret = preg_replace('/\?(.*)$/', "", $ret); - return $ret; - } - // Protocol relative urls (e.g. "//example.org/style.css") - if (strpos($url, '//') === 0) { - $ret .= substr($url, 2); - //remote urls with backslash in html/css are not really correct, but lets be genereous - } elseif ($url[0] === '/' || $url[0] === '\\') { - // Absolute path - $ret .= $host . $url; - } else { - // Relative path - //$base_path = $base_path !== "" ? rtrim($base_path, "/\\") . "/" : ""; - $ret .= $host . $base_path . $url; - } - return $ret; -} - - -/** - * parse a full url or pathname and return an array(protocol, host, path, - * file + query + fragment) - * - * @param string $url - * @return array - */ -function explode_url($url) { - $protocol = ""; - $host = ""; - $path = ""; - $file = ""; - - $arr = parse_url($url); - if ( isset($arr["scheme"])) { - $arr["scheme"] == mb_strtolower($arr["scheme"]); - } - - // Exclude windows drive letters... - if ( isset($arr["scheme"]) && $arr["scheme"] !== "file" && strlen($arr["scheme"]) > 1 ) { - $protocol = $arr["scheme"] . "://"; - - if ( isset($arr["user"]) ) { - $host .= $arr["user"]; - - if ( isset($arr["pass"]) ) { - $host .= ":" . $arr["pass"]; - } - - $host .= "@"; - } - - if ( isset($arr["host"]) ) { - $host .= $arr["host"]; - } - - if ( isset($arr["port"]) ) { - $host .= ":" . $arr["port"]; - } - - if ( isset($arr["path"]) && $arr["path"] !== "" ) { - // Do we have a trailing slash? - if ( $arr["path"][ mb_strlen($arr["path"]) - 1 ] === "/" ) { - $path = $arr["path"]; - $file = ""; - } - else { - $path = rtrim(dirname($arr["path"]), '/\\') . "/"; - $file = basename($arr["path"]); - } - } - - if ( isset($arr["query"]) ) { - $file .= "?" . $arr["query"]; - } - - if ( isset($arr["fragment"]) ) { - $file .= "#" . $arr["fragment"]; - } - - } - else { - - $i = mb_stripos($url, "file://"); - if ( $i !== false ) { - $url = mb_substr($url, $i + 7); - } - - $protocol = ""; // "file://"; ? why doesn't this work... It's because of - // network filenames like //COMPU/SHARENAME - - $host = ""; // localhost, really - $file = basename($url); - - $path = dirname($url); - - // Check that the path exists - if ( $path !== false ) { - $path .= '/'; - - } - else { - // generate a url to access the file if no real path found. - $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://'; - - $host = isset($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"] : php_uname("n"); - - if ( substr($arr["path"], 0, 1) === '/' ) { - $path = dirname($arr["path"]); - } - else { - $path = '/' . rtrim(dirname($_SERVER["SCRIPT_NAME"]), '/') . '/' . $arr["path"]; - } - } - } - - $ret = array($protocol, $host, $path, $file, - "protocol" => $protocol, - "host" => $host, - "path" => $path, - "file" => $file); - return $ret; -} - -/** - * Converts decimal numbers to roman numerals - * - * @param int $num - * - * @throws DOMPDF_Exception - * @return string - */ -function dec2roman($num) { - - static $ones = array("", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"); - static $tens = array("", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"); - static $hund = array("", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"); - static $thou = array("", "m", "mm", "mmm"); - - if ( !is_numeric($num) ) { - throw new DOMPDF_Exception("dec2roman() requires a numeric argument."); - } - - if ( $num > 4000 || $num < 0 ) { - return "(out of range)"; - } - - $num = strrev((string)$num); - - $ret = ""; - switch (mb_strlen($num)) { - case 4: $ret .= $thou[$num[3]]; - case 3: $ret .= $hund[$num[2]]; - case 2: $ret .= $tens[$num[1]]; - case 1: $ret .= $ones[$num[0]]; - default: break; - } - - return $ret; -} - -/** - * Determines whether $value is a percentage or not - * - * @param float $value - * - * @return bool - */ -function is_percent($value) { - return false !== mb_strpos($value, "%"); -} - -/** - * Parses a data URI scheme - * http://en.wikipedia.org/wiki/Data_URI_scheme - * - * @param string $data_uri The data URI to parse - * - * @return array The result with charset, mime type and decoded data - */ -function parse_data_uri($data_uri) { - if (!preg_match('/^data:(?P[a-z0-9\/+-.]+)(;charset=(?P[a-z0-9-])+)?(?P;base64)?\,(?P.*)?/i', $data_uri, $match)) { - return false; - } - - $match['data'] = rawurldecode($match['data']); - $result = array( - 'charset' => $match['charset'] ? $match['charset'] : 'US-ASCII', - 'mime' => $match['mime'] ? $match['mime'] : 'text/plain', - 'data' => $match['base64'] ? base64_decode($match['data']) : $match['data'], - ); - - return $result; -} - -/** - * mb_string compatibility - */ -if (!extension_loaded('mbstring')) { - def('MB_OVERLOAD_MAIL', 1); - def('MB_OVERLOAD_STRING', 2); - def('MB_OVERLOAD_REGEX', 4); - def('MB_CASE_UPPER', 0); - def('MB_CASE_LOWER', 1); - def('MB_CASE_TITLE', 2); - - if (!function_exists('mb_convert_encoding')) { - function mb_convert_encoding($data, $to_encoding, $from_encoding = 'UTF-8') { - if (str_replace('-', '', strtolower($to_encoding)) === 'utf8') { - return utf8_encode($data); - } - - return utf8_decode($data); - } - } - - if (!function_exists('mb_detect_encoding')) { - function mb_detect_encoding($data, $encoding_list = array('iso-8859-1'), $strict = false) { - return 'iso-8859-1'; - } - } - - if (!function_exists('mb_detect_order')) { - function mb_detect_order($encoding_list = array('iso-8859-1')) { - return 'iso-8859-1'; - } - } - - if (!function_exists('mb_internal_encoding')) { - function mb_internal_encoding($encoding = null) { - if (isset($encoding)) { - return true; - } - - return 'iso-8859-1'; - } - } - - if (!function_exists('mb_strlen')) { - function mb_strlen($str, $encoding = 'iso-8859-1') { - switch (str_replace('-', '', strtolower($encoding))) { - case "utf8": return strlen(utf8_encode($str)); - case "8bit": return strlen($str); - default: return strlen(utf8_decode($str)); - } - } - } - - if (!function_exists('mb_strpos')) { - function mb_strpos($haystack, $needle, $offset = 0) { - return strpos($haystack, $needle, $offset); - } - } - - if (!function_exists('mb_stripos')) { - function mb_stripos($haystack, $needle, $offset = 0) { - return stripos($haystack, $needle, $offset); - } - } - - if (!function_exists('mb_strrpos')) { - function mb_strrpos($haystack, $needle, $offset = 0) { - return strrpos($haystack, $needle, $offset); - } - } - - if (!function_exists('mb_strtolower')) { - function mb_strtolower( $str ) { - return strtolower($str); - } - } - - if (!function_exists('mb_strtoupper')) { - function mb_strtoupper( $str ) { - return strtoupper($str); - } - } - - if (!function_exists('mb_substr')) { - function mb_substr($string, $start, $length = null, $encoding = 'iso-8859-1') { - if ( is_null($length) ) { - return substr($string, $start); - } - - return substr($string, $start, $length); - } - } - - if (!function_exists('mb_substr_count')) { - function mb_substr_count($haystack, $needle, $encoding = 'iso-8859-1') { - return substr_count($haystack, $needle); - } - } - - if (!function_exists('mb_encode_numericentity')) { - function mb_encode_numericentity($str, $convmap, $encoding) { - return htmlspecialchars($str); - } - } - - if (!function_exists('mb_convert_case')) { - function mb_convert_case($str, $mode = MB_CASE_UPPER, $encoding = array()) { - switch($mode) { - case MB_CASE_UPPER: return mb_strtoupper($str); - case MB_CASE_LOWER: return mb_strtolower($str); - case MB_CASE_TITLE: return ucwords(mb_strtolower($str)); - default: return $str; - } - } - } - - if (!function_exists('mb_list_encodings')) { - function mb_list_encodings() { - return array( - "ISO-8859-1", - "UTF-8", - "8bit", - ); - } - } -} - -/** - * Decoder for RLE8 compression in windows bitmaps - * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp - * - * @param string $str Data to decode - * @param integer $width Image width - * - * @return string - */ -function rle8_decode ($str, $width){ - $lineWidth = $width + (3 - ($width-1) % 4); - $out = ''; - $cnt = strlen($str); - - for ($i = 0; $i <$cnt; $i++) { - $o = ord($str[$i]); - switch ($o){ - case 0: # ESCAPE - $i++; - switch (ord($str[$i])){ - case 0: # NEW LINE - $padCnt = $lineWidth - strlen($out)%$lineWidth; - if ($padCnt<$lineWidth) $out .= str_repeat(chr(0), $padCnt); # pad line - break; - case 1: # END OF FILE - $padCnt = $lineWidth - strlen($out)%$lineWidth; - if ($padCnt<$lineWidth) $out .= str_repeat(chr(0), $padCnt); # pad line - break 3; - case 2: # DELTA - $i += 2; - break; - default: # ABSOLUTE MODE - $num = ord($str[$i]); - for ($j = 0; $j < $num; $j++) - $out .= $str[++$i]; - if ($num % 2) $i++; - } - break; - default: - $out .= str_repeat($str[++$i], $o); - } - } - return $out; -} - -/** - * Decoder for RLE4 compression in windows bitmaps - * see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp - * - * @param string $str Data to decode - * @param integer $width Image width - * - * @return string - */ -function rle4_decode ($str, $width) { - $w = floor($width/2) + ($width % 2); - $lineWidth = $w + (3 - ( ($width-1) / 2) % 4); - $pixels = array(); - $cnt = strlen($str); - $c = 0; - - for ($i = 0; $i < $cnt; $i++) { - $o = ord($str[$i]); - switch ($o) { - case 0: # ESCAPE - $i++; - switch (ord($str[$i])){ - case 0: # NEW LINE - while (count($pixels)%$lineWidth != 0) { - $pixels[] = 0; - } - break; - case 1: # END OF FILE - while (count($pixels)%$lineWidth != 0) { - $pixels[] = 0; - } - break 3; - case 2: # DELTA - $i += 2; - break; - default: # ABSOLUTE MODE - $num = ord($str[$i]); - for ($j = 0; $j < $num; $j++) { - if ($j%2 == 0) { - $c = ord($str[++$i]); - $pixels[] = ($c & 240)>>4; - } - else { - $pixels[] = $c & 15; - } - } - - if ($num % 2 == 0) { - $i++; - } - } - break; - default: - $c = ord($str[++$i]); - for ($j = 0; $j < $o; $j++) { - $pixels[] = ($j%2==0 ? ($c & 240)>>4 : $c & 15); - } - } - } - - $out = ''; - if (count($pixels)%2) { - $pixels[] = 0; - } - - $cnt = count($pixels)/2; - - for ($i = 0; $i < $cnt; $i++) { - $out .= chr(16*$pixels[2*$i] + $pixels[2*$i+1]); - } - - return $out; -} - -if ( !function_exists("imagecreatefrombmp") ) { - -/** - * Credit goes to mgutt - * http://www.programmierer-forum.de/function-imagecreatefrombmp-welche-variante-laeuft-t143137.htm - * Modified by Fabien Menager to support RGB555 BMP format - */ -function imagecreatefrombmp($filename) { - if (!function_exists("imagecreatetruecolor")) { - trigger_error("The PHP GD extension is required, but is not installed.", E_ERROR); - return false; - } - - // version 1.00 - if (!($fh = fopen($filename, 'rb'))) { - trigger_error('imagecreatefrombmp: Can not open ' . $filename, E_USER_WARNING); - return false; - } - - $bytes_read = 0; - - // read file header - $meta = unpack('vtype/Vfilesize/Vreserved/Voffset', fread($fh, 14)); - - // check for bitmap - if ($meta['type'] != 19778) { - trigger_error('imagecreatefrombmp: ' . $filename . ' is not a bitmap!', E_USER_WARNING); - return false; - } - - // read image header - $meta += unpack('Vheadersize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vcolors/Vimportant', fread($fh, 40)); - $bytes_read += 40; - - // read additional bitfield header - if ($meta['compression'] == 3) { - $meta += unpack('VrMask/VgMask/VbMask', fread($fh, 12)); - $bytes_read += 12; - } - - // set bytes and padding - $meta['bytes'] = $meta['bits'] / 8; - $meta['decal'] = 4 - (4 * (($meta['width'] * $meta['bytes'] / 4)- floor($meta['width'] * $meta['bytes'] / 4))); - if ($meta['decal'] == 4) { - $meta['decal'] = 0; - } - - // obtain imagesize - if ($meta['imagesize'] < 1) { - $meta['imagesize'] = $meta['filesize'] - $meta['offset']; - // in rare cases filesize is equal to offset so we need to read physical size - if ($meta['imagesize'] < 1) { - $meta['imagesize'] = @filesize($filename) - $meta['offset']; - if ($meta['imagesize'] < 1) { - trigger_error('imagecreatefrombmp: Can not obtain filesize of ' . $filename . '!', E_USER_WARNING); - return false; - } - } - } - - // calculate colors - $meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors']; - - // read color palette - $palette = array(); - if ($meta['bits'] < 16) { - $palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4)); - // in rare cases the color value is signed - if ($palette[1] < 0) { - foreach ($palette as $i => $color) { - $palette[$i] = $color + 16777216; - } - } - } - - // ignore extra bitmap headers - if ($meta['headersize'] > $bytes_read) { - fread($fh, $meta['headersize'] - $bytes_read); - } - - // create gd image - $im = imagecreatetruecolor($meta['width'], $meta['height']); - $data = fread($fh, $meta['imagesize']); - - // uncompress data - switch ($meta['compression']) { - case 1: $data = rle8_decode($data, $meta['width']); break; - case 2: $data = rle4_decode($data, $meta['width']); break; - } - - $p = 0; - $vide = chr(0); - $y = $meta['height'] - 1; - $error = 'imagecreatefrombmp: ' . $filename . ' has not enough data!'; - - // loop through the image data beginning with the lower left corner - while ($y >= 0) { - $x = 0; - while ($x < $meta['width']) { - switch ($meta['bits']) { - case 32: - case 24: - if (!($part = substr($data, $p, 3 /*$meta['bytes']*/))) { - trigger_error($error, E_USER_WARNING); - return $im; - } - $color = unpack('V', $part . $vide); - break; - case 16: - if (!($part = substr($data, $p, 2 /*$meta['bytes']*/))) { - trigger_error($error, E_USER_WARNING); - return $im; - } - $color = unpack('v', $part); - - if (empty($meta['rMask']) || $meta['rMask'] != 0xf800) { - $color[1] = (($color[1] & 0x7c00) >> 7) * 65536 + (($color[1] & 0x03e0) >> 2) * 256 + (($color[1] & 0x001f) << 3); // 555 - } - else { - $color[1] = (($color[1] & 0xf800) >> 8) * 65536 + (($color[1] & 0x07e0) >> 3) * 256 + (($color[1] & 0x001f) << 3); // 565 - } - break; - case 8: - $color = unpack('n', $vide . substr($data, $p, 1)); - $color[1] = $palette[ $color[1] + 1 ]; - break; - case 4: - $color = unpack('n', $vide . substr($data, floor($p), 1)); - $color[1] = ($p * 2) % 2 == 0 ? $color[1] >> 4 : $color[1] & 0x0F; - $color[1] = $palette[ $color[1] + 1 ]; - break; - case 1: - $color = unpack('n', $vide . substr($data, floor($p), 1)); - switch (($p * 8) % 8) { - case 0: $color[1] = $color[1] >> 7; break; - case 1: $color[1] = ($color[1] & 0x40) >> 6; break; - case 2: $color[1] = ($color[1] & 0x20) >> 5; break; - case 3: $color[1] = ($color[1] & 0x10) >> 4; break; - case 4: $color[1] = ($color[1] & 0x8 ) >> 3; break; - case 5: $color[1] = ($color[1] & 0x4 ) >> 2; break; - case 6: $color[1] = ($color[1] & 0x2 ) >> 1; break; - case 7: $color[1] = ($color[1] & 0x1 ); break; - } - $color[1] = $palette[ $color[1] + 1 ]; - break; - default: - trigger_error('imagecreatefrombmp: ' . $filename . ' has ' . $meta['bits'] . ' bits and this is not supported!', E_USER_WARNING); - return false; - } - imagesetpixel($im, $x, $y, $color[1]); - $x++; - $p += $meta['bytes']; - } - $y--; - $p += $meta['decal']; - } - fclose($fh); - return $im; -} -} - -/** - * getimagesize doesn't give a good size for 32bit BMP image v5 - * - * @param string $filename - * @return array The same format as getimagesize($filename) - */ -function dompdf_getimagesize($filename, $context = null) { - static $cache = array(); - - if ( isset($cache[$filename]) ) { - return $cache[$filename]; - } - - list($width, $height, $type) = getimagesize($filename); - - if ( $width == null || $height == null ) { - $data = file_get_contents($filename, null, $context, 0, 26); - - if ( substr($data, 0, 2) === "BM" ) { - $meta = unpack('vtype/Vfilesize/Vreserved/Voffset/Vheadersize/Vwidth/Vheight', $data); - $width = (int)$meta['width']; - $height = (int)$meta['height']; - $type = IMAGETYPE_BMP; - } - } - - return $cache[$filename] = array($width, $height, $type); -} - -/** - * Converts a CMYK color to RGB - * - * @param float|float[] $c - * @param float $m - * @param float $y - * @param float $k - * - * @return float[] - */ -function cmyk_to_rgb($c, $m = null, $y = null, $k = null) { - if (is_array($c)) { - list($c, $m, $y, $k) = $c; - } - - $c *= 255; - $m *= 255; - $y *= 255; - $k *= 255; - - $r = (1 - round(2.55 * ($c+$k))) ; - $g = (1 - round(2.55 * ($m+$k))) ; - $b = (1 - round(2.55 * ($y+$k))) ; - - if ($r < 0) $r = 0; - if ($g < 0) $g = 0; - if ($b < 0) $b = 0; - - return array( - $r, $g, $b, - "r" => $r, "g" => $g, "b" => $b - ); -} - -function unichr($c) { - if ($c <= 0x7F) { - return chr($c); - } - else if ($c <= 0x7FF) { - return chr(0xC0 | $c >> 6) . chr(0x80 | $c & 0x3F); - } - else if ($c <= 0xFFFF) { - return chr(0xE0 | $c >> 12) . chr(0x80 | $c >> 6 & 0x3F) - . chr(0x80 | $c & 0x3F); - } - else if ($c <= 0x10FFFF) { - return chr(0xF0 | $c >> 18) . chr(0x80 | $c >> 12 & 0x3F) - . chr(0x80 | $c >> 6 & 0x3F) - . chr(0x80 | $c & 0x3F); - } - return false; -} - -if ( !function_exists("date_default_timezone_get") ) { - function date_default_timezone_get() { - return ""; - } - - function date_default_timezone_set($timezone_identifier) { - return true; - } -} - -/** - * Stores warnings in an array for display later - * This function allows warnings generated by the DomDocument parser - * and CSS loader ({@link Stylesheet}) to be captured and displayed - * later. Without this function, errors are displayed immediately and - * PDF streaming is impossible. - * @see http://www.php.net/manual/en/function.set-error_handler.php - * - * @param int $errno - * @param string $errstr - * @param string $errfile - * @param string $errline - * - * @throws DOMPDF_Exception - */ -function record_warnings($errno, $errstr, $errfile, $errline) { - - // Not a warning or notice - if ( !($errno & (E_WARNING | E_NOTICE | E_USER_NOTICE | E_USER_WARNING )) ) { - throw new DOMPDF_Exception($errstr . " $errno"); - } - - global $_dompdf_warnings; - global $_dompdf_show_warnings; - - if ( $_dompdf_show_warnings ) { - echo $errstr . "\n"; - } - - $_dompdf_warnings[] = $errstr; -} - -/** - * Print a useful backtrace - */ -function bt() { - if ( php_sapi_name() !== "cli") { - echo "
";
-  }
-    
-  $bt = debug_backtrace();
-
-  array_shift($bt); // remove actual bt() call
-  echo "\n";
-
-  $i = 0;
-  foreach ($bt as $call) {
-    $file = basename($call["file"]) . " (" . $call["line"] . ")";
-    if ( isset($call["class"]) ) {
-      $func = $call["class"] . "->" . $call["function"] . "()";
-    }
-    else {
-      $func = $call["function"] . "()";
-    }
-
-    echo "#" . str_pad($i, 2, " ", STR_PAD_RIGHT) . ": " . str_pad($file.":", 42) . " $func\n";
-    $i++;
-  }
-  echo "\n";
-  
-  if ( php_sapi_name() !== "cli") {
-    echo "
"; - } -} - -/** - * Print debug messages - * - * @param string $type The type of debug messages to print - * @param string $msg The message to show - */ -function dompdf_debug($type, $msg) { - global $_DOMPDF_DEBUG_TYPES, $_dompdf_show_warnings, $_dompdf_debug; - if ( isset($_DOMPDF_DEBUG_TYPES[$type]) && ($_dompdf_show_warnings || $_dompdf_debug) ) { - $arr = debug_backtrace(); - - echo basename($arr[0]["file"]) . " (" . $arr[0]["line"] ."): " . $arr[1]["function"] . ": "; - pre_r($msg); - } -} - -if ( !function_exists("print_memusage") ) { -/** - * Dump memory usage - */ -function print_memusage() { - global $memusage; - echo "Memory Usage\n"; - $prev = 0; - $initial = reset($memusage); - echo str_pad("Initial:", 40) . $initial . "\n\n"; - - foreach ($memusage as $key=>$mem) { - $mem -= $initial; - echo str_pad("$key:" , 40); - echo str_pad("$mem", 12) . "(diff: " . ($mem - $prev) . ")\n"; - $prev = $mem; - } - - echo "\n" . str_pad("Total:", 40) . memory_get_usage() . "\n"; -} -} - -if ( !function_exists("enable_mem_profile") ) { -/** - * Initialize memory profiling code - */ -function enable_mem_profile() { - global $memusage; - $memusage = array("Startup" => memory_get_usage()); - register_shutdown_function("print_memusage"); -} -} - -if ( !function_exists("mark_memusage") ) { -/** - * Record the current memory usage - * - * @param string $location a meaningful location - */ -function mark_memusage($location) { - global $memusage; - if ( isset($memusage) ) { - $memusage[$location] = memory_get_usage(); - } -} -} - -if ( !function_exists('sys_get_temp_dir')) { -/** - * Find the current system temporary directory - * - * @link http://us.php.net/manual/en/function.sys-get-temp-dir.php#85261 - */ -function sys_get_temp_dir() { - if (!empty($_ENV['TMP'])) { - return realpath($_ENV['TMP']); - } - - if (!empty($_ENV['TMPDIR'])) { - return realpath( $_ENV['TMPDIR']); - } - - if (!empty($_ENV['TEMP'])) { - return realpath( $_ENV['TEMP']); - } - - $tempfile=tempnam(uniqid(rand(), true), ''); - if (file_exists($tempfile)) { - unlink($tempfile); - return realpath(dirname($tempfile)); - } -} -} - -if ( function_exists("memory_get_peak_usage") ) { - function DOMPDF_memory_usage(){ - return memory_get_peak_usage(true); - } -} -else if ( function_exists("memory_get_usage") ) { - function DOMPDF_memory_usage(){ - return memory_get_usage(true); - } -} -else { - function DOMPDF_memory_usage(){ - return "N/A"; - } -} - - -/** - * Affect null to the unused objects - * @param mixed $object - */ -if ( PHP_VERSION_ID < 50300 ) { - function clear_object(&$object) { - if ( is_object($object) ) { - foreach ($object as &$value) { - clear_object($value); - } - } - - $object = null; - unset($object); - } -} -else { - function clear_object(&$object) { - // void - } -} diff --git a/library/vendor/dompdf/include/gd_adapter.cls.php b/library/vendor/dompdf/include/gd_adapter.cls.php deleted file mode 100644 index 1eab04cc4..000000000 --- a/library/vendor/dompdf/include/gd_adapter.cls.php +++ /dev/null @@ -1,840 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Image rendering interface - * - * Renders to an image format supported by GD (jpeg, gif, png, xpm). - * Not super-useful day-to-day but handy nonetheless - * - * @package dompdf - */ -class GD_Adapter implements Canvas { - /** - * @var DOMPDF - */ - private $_dompdf; - - /** - * Resource handle for the image - * - * @var resource - */ - private $_img; - - /** - * Image width in pixels - * - * @var int - */ - private $_width; - - /** - * Image height in pixels - * - * @var int - */ - private $_height; - - /** - * Current page number - * - * @var int - */ - private $_page_number; - - /** - * Total number of pages - * - * @var int - */ - private $_page_count; - - /** - * Image antialias factor - * - * @var float - */ - private $_aa_factor; - - /** - * Allocated colors - * - * @var array - */ - private $_colors; - - /** - * Background color - * - * @var int - */ - private $_bg_color; - - /** - * Class constructor - * - * @param mixed $size The size of image to create: array(x1,y1,x2,y2) or "letter", "legal", etc. - * @param string $orientation The orientation of the document (either 'landscape' or 'portrait') - * @param DOMPDF $dompdf - * @param float $aa_factor Anti-aliasing factor, 1 for no AA - * @param array $bg_color Image background color: array(r,g,b,a), 0 <= r,g,b,a <= 1 - */ - function __construct($size, $orientation = "portrait", DOMPDF $dompdf, $aa_factor = 1.0, $bg_color = array(1,1,1,0) ) { - - if ( !is_array($size) ) { - $size = strtolower($size); - - if ( isset(CPDF_Adapter::$PAPER_SIZES[$size]) ) { - $size = CPDF_Adapter::$PAPER_SIZES[$size]; - } - else { - $size = CPDF_Adapter::$PAPER_SIZES["letter"]; - } - } - - if ( strtolower($orientation) === "landscape" ) { - list($size[2],$size[3]) = array($size[3],$size[2]); - } - - $this->_dompdf = $dompdf; - - if ( $aa_factor < 1 ) { - $aa_factor = 1; - } - - $this->_aa_factor = $aa_factor; - - $size[2] *= $aa_factor; - $size[3] *= $aa_factor; - - $this->_width = $size[2] - $size[0]; - $this->_height = $size[3] - $size[1]; - - $this->_img = imagecreatetruecolor($this->_width, $this->_height); - - if ( is_null($bg_color) || !is_array($bg_color) ) { - // Pure white bg - $bg_color = array(1,1,1,0); - } - - $this->_bg_color = $this->_allocate_color($bg_color); - imagealphablending($this->_img, true); - imagesavealpha($this->_img, true); - imagefill($this->_img, 0, 0, $this->_bg_color); - - } - - function get_dompdf(){ - return $this->_dompdf; - } - - /** - * Return the GF image resource - * - * @return resource - */ - function get_image() { return $this->_img; } - - /** - * Return the image's width in pixels - * - * @return float - */ - function get_width() { return $this->_width / $this->_aa_factor; } - - /** - * Return the image's height in pixels - * - * @return float - */ - function get_height() { return $this->_height / $this->_aa_factor; } - - /** - * Returns the current page number - * @return int - */ - function get_page_number() { return $this->_page_number; } - - /** - * Returns the total number of pages in the document - * @return int - */ - function get_page_count() { return $this->_page_count; } - - /** - * Sets the current page number - * - * @param int $num - */ - function set_page_number($num) { $this->_page_number = $num; } - - /** - * Sets the page count - * - * @param int $count - */ - function set_page_count($count) { $this->_page_count = $count; } - - /** - * Sets the opacity - * - * @param $opacity - * @param $mode - */ - function set_opacity($opacity, $mode = "Normal") { - // FIXME - } - - /** - * Allocate a new color. Allocate with GD as needed and store - * previously allocated colors in $this->_colors. - * - * @param array $color The new current color - * @return int The allocated color - */ - private function _allocate_color($color) { - - if ( isset($color["c"]) ) { - $color = cmyk_to_rgb($color); - } - - // Full opacity if no alpha set - if ( !isset($color[3]) ) - $color[3] = 0; - - list($r,$g,$b,$a) = $color; - - $r *= 255; - $g *= 255; - $b *= 255; - $a *= 127; - - // Clip values - $r = $r > 255 ? 255 : $r; - $g = $g > 255 ? 255 : $g; - $b = $b > 255 ? 255 : $b; - $a = $a > 127 ? 127 : $a; - - $r = $r < 0 ? 0 : $r; - $g = $g < 0 ? 0 : $g; - $b = $b < 0 ? 0 : $b; - $a = $a < 0 ? 0 : $a; - - $key = sprintf("#%02X%02X%02X%02X", $r, $g, $b, $a); - - if ( isset($this->_colors[$key]) ) - return $this->_colors[$key]; - - if ( $a != 0 ) - $this->_colors[$key] = imagecolorallocatealpha($this->_img, $r, $g, $b, $a); - else - $this->_colors[$key] = imagecolorallocate($this->_img, $r, $g, $b); - - return $this->_colors[$key]; - - } - - /** - * Draws a line from x1,y1 to x2,y2 - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the format of the - * $style parameter (aka dash). - * - * @param float $x1 - * @param float $y1 - * @param float $x2 - * @param float $y2 - * @param array $color - * @param float $width - * @param array $style - */ - function line($x1, $y1, $x2, $y2, $color, $width, $style = null) { - - // Scale by the AA factor - $x1 *= $this->_aa_factor; - $y1 *= $this->_aa_factor; - $x2 *= $this->_aa_factor; - $y2 *= $this->_aa_factor; - $width *= $this->_aa_factor; - - $c = $this->_allocate_color($color); - - // Convert the style array if required - if ( !is_null($style) ) { - $gd_style = array(); - - if ( count($style) == 1 ) { - for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) { - $gd_style[] = $c; - } - - for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) { - $gd_style[] = $this->_bg_color; - } - - } else { - - $i = 0; - foreach ($style as $length) { - - if ( $i % 2 == 0 ) { - // 'On' pattern - for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) - $gd_style[] = $c; - - } else { - // Off pattern - for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) - $gd_style[] = $this->_bg_color; - - } - $i++; - } - } - - imagesetstyle($this->_img, $gd_style); - $c = IMG_COLOR_STYLED; - } - - imagesetthickness($this->_img, $width); - - imageline($this->_img, $x1, $y1, $x2, $y2, $c); - - } - - function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = array()) { - // @todo - } - - /** - * Draws a rectangle at x1,y1 with width w and height h - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the $style - * parameter (aka dash) - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - * @param array $color - * @param float $width - * @param array $style - */ - function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) { - - // Scale by the AA factor - $x1 *= $this->_aa_factor; - $y1 *= $this->_aa_factor; - $w *= $this->_aa_factor; - $h *= $this->_aa_factor; - - $c = $this->_allocate_color($color); - - // Convert the style array if required - if ( !is_null($style) ) { - $gd_style = array(); - - foreach ($style as $length) { - for ($i = 0; $i < $length; $i++) { - $gd_style[] = $c; - } - } - - imagesetstyle($this->_img, $gd_style); - $c = IMG_COLOR_STYLED; - } - - imagesetthickness($this->_img, $width); - - imagerectangle($this->_img, $x1, $y1, $x1 + $w, $y1 + $h, $c); - - } - - /** - * Draws a filled rectangle at x1,y1 with width w and height h - * - * See {@link Style::munge_color()} for the format of the color array. - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - * @param array $color - */ - function filled_rectangle($x1, $y1, $w, $h, $color) { - - // Scale by the AA factor - $x1 *= $this->_aa_factor; - $y1 *= $this->_aa_factor; - $w *= $this->_aa_factor; - $h *= $this->_aa_factor; - - $c = $this->_allocate_color($color); - - imagefilledrectangle($this->_img, $x1, $y1, $x1 + $w, $y1 + $h, $c); - - } - - /** - * Starts a clipping rectangle at x1,y1 with width w and height h - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - */ - function clipping_rectangle($x1, $y1, $w, $h) { - // @todo - } - - function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) { - // @todo - } - - /** - * Ends the last clipping shape - */ - function clipping_end() { - // @todo - } - - function save() { - // @todo - } - - function restore() { - // @todo - } - - function rotate($angle, $x, $y) { - // @todo - } - - function skew($angle_x, $angle_y, $x, $y) { - // @todo - } - - function scale($s_x, $s_y, $x, $y) { - // @todo - } - - function translate($t_x, $t_y) { - // @todo - } - - function transform($a, $b, $c, $d, $e, $f) { - // @todo - } - - /** - * Draws a polygon - * - * The polygon is formed by joining all the points stored in the $points - * array. $points has the following structure: - * - * array(0 => x1, - * 1 => y1, - * 2 => x2, - * 3 => y2, - * ... - * ); - * - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the $style - * parameter (aka dash) - * - * @param array $points - * @param array $color - * @param float $width - * @param array $style - * @param bool $fill Fills the polygon if true - */ - function polygon($points, $color, $width = null, $style = null, $fill = false) { - - // Scale each point by the AA factor - foreach (array_keys($points) as $i) - $points[$i] *= $this->_aa_factor; - - $c = $this->_allocate_color($color); - - // Convert the style array if required - if ( !is_null($style) && !$fill ) { - $gd_style = array(); - - foreach ($style as $length) { - for ($i = 0; $i < $length; $i++) { - $gd_style[] = $c; - } - } - - imagesetstyle($this->_img, $gd_style); - $c = IMG_COLOR_STYLED; - } - - imagesetthickness($this->_img, $width); - - if ( $fill ) - imagefilledpolygon($this->_img, $points, count($points) / 2, $c); - else - imagepolygon($this->_img, $points, count($points) / 2, $c); - - } - - /** - * Draws a circle at $x,$y with radius $r - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the $style - * parameter (aka dash) - * - * @param float $x - * @param float $y - * @param float $r - * @param array $color - * @param float $width - * @param array $style - * @param bool $fill Fills the circle if true - */ - function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false) { - - // Scale by the AA factor - $x *= $this->_aa_factor; - $y *= $this->_aa_factor; - $r *= $this->_aa_factor; - - $c = $this->_allocate_color($color); - - // Convert the style array if required - if ( !is_null($style) && !$fill ) { - $gd_style = array(); - - foreach ($style as $length) { - for ($i = 0; $i < $length; $i++) { - $gd_style[] = $c; - } - } - - imagesetstyle($this->_img, $gd_style); - $c = IMG_COLOR_STYLED; - } - - imagesetthickness($this->_img, $width); - - if ( $fill ) - imagefilledellipse($this->_img, $x, $y, $r, $r, $c); - else - imageellipse($this->_img, $x, $y, $r, $r, $c); - - } - - /** - * Add an image to the pdf. - * The image is placed at the specified x and y coordinates with the - * given width and height. - * - * @param string $img_url the path to the image - * @param float $x x position - * @param float $y y position - * @param int $w width (in pixels) - * @param int $h height (in pixels) - * @param string $resolution - * - * @return void - * @internal param string $img_type the type (e.g. extension) of the image - */ - function image($img_url, $x, $y, $w, $h, $resolution = "normal") { - $img_type = Image_Cache::detect_type($img_url, $this->_dompdf->get_http_context()); - $img_ext = Image_Cache::type_to_ext($img_type); - - if ( !$img_ext ) { - return; - } - - $func = "imagecreatefrom$img_ext"; - $src = @$func($img_url); - - if ( !$src ) { - return; // Probably should add to $_dompdf_errors or whatever here - } - - // Scale by the AA factor - $x *= $this->_aa_factor; - $y *= $this->_aa_factor; - - $w *= $this->_aa_factor; - $h *= $this->_aa_factor; - - $img_w = imagesx($src); - $img_h = imagesy($src); - - imagecopyresampled($this->_img, $src, $x, $y, 0, 0, $w, $h, $img_w, $img_h); - - } - - /** - * Writes text at the specified x and y coordinates - * See {@link Style::munge_color()} for the format of the color array. - * - * @param float $x - * @param float $y - * @param string $text the text to write - * @param string $font the font file to use - * @param float $size the font size, in points - * @param array $color - * @param float $word_spacing word spacing adjustment - * @param float $char_spacing - * @param float $angle Text angle - * - * @return void - */ - function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_spacing = 0.0, $char_spacing = 0.0, $angle = 0.0) { - - // Scale by the AA factor - $x *= $this->_aa_factor; - $y *= $this->_aa_factor; - $size *= $this->_aa_factor; - - $h = $this->get_font_height($font, $size); - $c = $this->_allocate_color($color); - - $text = mb_encode_numericentity($text, array(0x0080, 0xff, 0, 0xff), 'UTF-8'); - - $font = $this->get_ttf_file($font); - - // FIXME: word spacing - @imagettftext($this->_img, $size, $angle, $x, $y + $h, $c, $font, $text); - - } - - function javascript($code) { - // Not implemented - } - - /** - * Add a named destination (similar to ... in html) - * - * @param string $anchorname The name of the named destination - */ - function add_named_dest($anchorname) { - // Not implemented - } - - /** - * Add a link to the pdf - * - * @param string $url The url to link to - * @param float $x The x position of the link - * @param float $y The y position of the link - * @param float $width The width of the link - * @param float $height The height of the link - */ - function add_link($url, $x, $y, $width, $height) { - // Not implemented - } - - /** - * Add meta information to the PDF - * - * @param string $label label of the value (Creator, Producer, etc.) - * @param string $value the text to set - */ - function add_info($label, $value) { - // N/A - } - - function set_default_view($view, $options = array()) { - // N/A - } - - /** - * Calculates text size, in points - * - * @param string $text the text to be sized - * @param string $font the desired font - * @param float $size the desired font size - * @param float $word_spacing word spacing, if any - * @param float $char_spacing char spacing, if any - * - * @return float - */ - function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) { - $font = $this->get_ttf_file($font); - - $text = mb_encode_numericentity($text, array(0x0080, 0xffff, 0, 0xffff), 'UTF-8'); - - // FIXME: word spacing - list($x1,,$x2) = @imagettfbbox($size, 0, $font, $text); - return $x2 - $x1; - } - - function get_ttf_file($font) { - if ( strpos($font, '.ttf') === false ) - $font .= ".ttf"; - - /*$filename = substr(strtolower(basename($font)), 0, -4); - - if ( in_array($filename, DOMPDF::$native_fonts) ) { - return "arial.ttf"; - }*/ - - return $font; - } - - /** - * Calculates font height, in points - * - * @param string $font - * @param float $size - * @return float - */ - function get_font_height($font, $size) { - $font = $this->get_ttf_file($font); - $ratio = $this->_dompdf->get_option("font_height_ratio"); - - // FIXME: word spacing - list(,$y2,,,,$y1) = imagettfbbox($size, 0, $font, "MXjpqytfhl"); // Test string with ascenders, descenders and caps - return ($y2 - $y1) * $ratio; - } - - function get_font_baseline($font, $size) { - $ratio = $this->_dompdf->get_option("font_height_ratio"); - return $this->get_font_height($font, $size) / $ratio; - } - - /** - * Starts a new page - * - * Subsequent drawing operations will appear on the new page. - */ - function new_page() { - $this->_page_number++; - $this->_page_count++; - } - - function open_object(){ - // N/A - } - - function close_object(){ - // N/A - } - - function add_object(){ - // N/A - } - - function page_text(){ - // N/A - } - - /** - * Streams the image directly to the browser - * - * @param string $filename the name of the image file (ignored) - * @param array $options associative array, 'type' => jpeg|jpg|png, 'quality' => 0 - 100 (jpeg only) - */ - function stream($filename, $options = null) { - - // Perform any antialiasing - if ( $this->_aa_factor != 1 ) { - $dst_w = $this->_width / $this->_aa_factor; - $dst_h = $this->_height / $this->_aa_factor; - $dst = imagecreatetruecolor($dst_w, $dst_h); - imagecopyresampled($dst, $this->_img, 0, 0, 0, 0, - $dst_w, $dst_h, - $this->_width, $this->_height); - } else { - $dst = $this->_img; - } - - if ( !isset($options["type"]) ) - $options["type"] = "png"; - - $type = strtolower($options["type"]); - - header("Cache-Control: private"); - - switch ($type) { - - case "jpg": - case "jpeg": - if ( !isset($options["quality"]) ) - $options["quality"] = 75; - - header("Content-type: image/jpeg"); - imagejpeg($dst, '', $options["quality"]); - break; - - case "png": - default: - header("Content-type: image/png"); - imagepng($dst); - break; - } - - if ( $this->_aa_factor != 1 ) - imagedestroy($dst); - } - - /** - * Returns the PNG as a string - * - * @param array $options associative array, 'type' => jpeg|jpg|png, 'quality' => 0 - 100 (jpeg only) - * @return string - */ - function output($options = null) { - - if ( $this->_aa_factor != 1 ) { - $dst_w = $this->_width / $this->_aa_factor; - $dst_h = $this->_height / $this->_aa_factor; - $dst = imagecreatetruecolor($dst_w, $dst_h); - imagecopyresampled($dst, $this->_img, 0, 0, 0, 0, - $dst_w, $dst_h, - $this->_width, $this->_height); - } else { - $dst = $this->_img; - } - - if ( !isset($options["type"]) ) - $options["type"] = "png"; - - $type = $options["type"]; - - ob_start(); - - switch ($type) { - - case "jpg": - case "jpeg": - if ( !isset($options["quality"]) ) - $options["quality"] = 75; - - imagejpeg($dst, '', $options["quality"]); - break; - - case "png": - default: - imagepng($dst); - break; - } - - $image = ob_get_clean(); - - if ( $this->_aa_factor != 1 ) - imagedestroy($dst); - - return $image; - } - - -} diff --git a/library/vendor/dompdf/include/image_cache.cls.php b/library/vendor/dompdf/include/image_cache.cls.php deleted file mode 100644 index e7175c4dc..000000000 --- a/library/vendor/dompdf/include/image_cache.cls.php +++ /dev/null @@ -1,185 +0,0 @@ - - * @author Helmut Tischer - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Static class that resolves image urls and downloads and caches - * remote images if required. - * - * @access private - * @package dompdf - */ -class Image_Cache { - - /** - * Array of downloaded images. Cached so that identical images are - * not needlessly downloaded. - * - * @var array - */ - static protected $_cache = array(); - - /** - * The url to the "broken image" used when images can't be loade - * - * @var string - */ - public static $broken_image; - - /** - * Resolve and fetch an image for use. - * - * @param string $url The url of the image - * @param string $protocol Default protocol if none specified in $url - * @param string $host Default host if none specified in $url - * @param string $base_path Default path if none specified in $url - * @param DOMPDF $dompdf The DOMPDF instance - * - * @throws DOMPDF_Image_Exception - * @return array An array with two elements: The local path to the image and the image extension - */ - static function resolve_url($url, $protocol, $host, $base_path, DOMPDF $dompdf) { - $protocol = mb_strtolower($protocol); - $parsed_url = explode_url($url); - $message = null; - - $remote = ($protocol && $protocol !== "file://") || ($parsed_url['protocol'] != ""); - - $data_uri = strpos($parsed_url['protocol'], "data:") === 0; - $full_url = null; - $enable_remote = $dompdf->get_option("enable_remote"); - - try { - - // Remote not allowed and is not DataURI - if ( !$enable_remote && $remote && !$data_uri ) { - throw new DOMPDF_Image_Exception("DOMPDF_ENABLE_REMOTE is set to FALSE"); - } - - // Remote allowed or DataURI - else if ( $enable_remote && $remote || $data_uri ) { - // Download remote files to a temporary directory - $full_url = build_url($protocol, $host, $base_path, $url); - - // From cache - if ( isset(self::$_cache[$full_url]) ) { - $resolved_url = self::$_cache[$full_url]; - } - - // From remote - else { - $tmp_dir = $dompdf->get_option("temp_dir"); - $resolved_url = tempnam($tmp_dir, "ca_dompdf_img_"); - $image = ""; - - if ($data_uri) { - if ($parsed_data_uri = parse_data_uri($url)) { - $image = $parsed_data_uri['data']; - } - } - else { - set_error_handler("record_warnings"); - $image = file_get_contents($full_url, null, $dompdf->get_http_context()); - restore_error_handler(); - } - - // Image not found or invalid - if ( strlen($image) == 0 ) { - $msg = ($data_uri ? "Data-URI could not be parsed" : "Image not found"); - throw new DOMPDF_Image_Exception($msg); - } - - // Image found, put in cache and process - else { - //e.g. fetch.php?media=url.jpg&cache=1 - //- Image file name might be one of the dynamic parts of the url, don't strip off! - //- a remote url does not need to have a file extension at all - //- local cached file does not have a matching file extension - //Therefore get image type from the content - file_put_contents($resolved_url, $image); - } - } - } - - // Not remote, local image - else { - $resolved_url = build_url($protocol, $host, $base_path, $url); - } - - // Check if the local file is readable - if ( !is_readable($resolved_url) || !filesize($resolved_url) ) { - throw new DOMPDF_Image_Exception("Image not readable or empty"); - } - - // Check is the file is an image - else { - list($width, $height, $type) = dompdf_getimagesize($resolved_url, $dompdf->get_http_context()); - - // Known image type - if ( $width && $height && in_array($type, array(IMAGETYPE_GIF, IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_BMP)) ) { - //Don't put replacement image into cache - otherwise it will be deleted on cache cleanup. - //Only execute on successful caching of remote image. - if ( $enable_remote && $remote || $data_uri ) { - self::$_cache[$full_url] = $resolved_url; - } - } - - // Unknown image type - else { - throw new DOMPDF_Image_Exception("Image type unknown"); - } - } - } - catch(DOMPDF_Image_Exception $e) { - $resolved_url = self::$broken_image; - $type = IMAGETYPE_PNG; - $message = "Image not found or type unknown"; - $_dompdf_warnings[] = $e->getMessage()." :: $url"; - } - - return array($resolved_url, $type, $message); - } - - /** - * Unlink all cached images (i.e. temporary images either downloaded - * or converted) - */ - static function clear() { - if ( empty(self::$_cache) || DEBUGKEEPTEMP ) return; - - foreach ( self::$_cache as $file ) { - if (DEBUGPNG) print "[clear unlink $file]"; - unlink($file); - } - - self::$_cache = array(); - } - - static function detect_type($file, $context = null) { - list(, , $type) = dompdf_getimagesize($file, $context); - return $type; - } - - static function type_to_ext($type) { - $image_types = array( - IMAGETYPE_GIF => "gif", - IMAGETYPE_PNG => "png", - IMAGETYPE_JPEG => "jpeg", - IMAGETYPE_BMP => "bmp", - ); - - return (isset($image_types[$type]) ? $image_types[$type] : null); - } - - static function is_broken($url) { - return $url === self::$broken_image; - } -} - -Image_Cache::$broken_image = DOMPDF_LIB_DIR . "/res/broken_image.png"; diff --git a/library/vendor/dompdf/include/image_frame_decorator.cls.php b/library/vendor/dompdf/include/image_frame_decorator.cls.php deleted file mode 100644 index b5a7983aa..000000000 --- a/library/vendor/dompdf/include/image_frame_decorator.cls.php +++ /dev/null @@ -1,80 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Decorates frames for image layout and rendering - * - * @access private - * @package dompdf - */ -class Image_Frame_Decorator extends Frame_Decorator { - - /** - * The path to the image file (note that remote images are - * downloaded locally to DOMPDF_TEMP_DIR). - * - * @var string - */ - protected $_image_url; - - /** - * The image's file error message - * - * @var string - */ - protected $_image_msg; - - /** - * Class constructor - * - * @param Frame $frame the frame to decorate - * @param DOMPDF $dompdf the document's dompdf object (required to resolve relative & remote urls) - */ - function __construct(Frame $frame, DOMPDF $dompdf) { - parent::__construct($frame, $dompdf); - $url = $frame->get_node()->getAttribute("src"); - - $debug_png = $dompdf->get_option("debug_png"); - if ($debug_png) print '[__construct '.$url.']'; - - list($this->_image_url, /*$type*/, $this->_image_msg) = Image_Cache::resolve_url( - $url, - $dompdf->get_protocol(), - $dompdf->get_host(), - $dompdf->get_base_path(), - $dompdf - ); - - if ( Image_Cache::is_broken($this->_image_url) && - $alt = $frame->get_node()->getAttribute("alt") ) { - $style = $frame->get_style(); - $style->width = (4/3)*Font_Metrics::get_text_width($alt, $style->font_family, $style->font_size, $style->word_spacing); - $style->height = Font_Metrics::get_font_height($style->font_family, $style->font_size); - } - } - - /** - * Return the image's url - * - * @return string The url of this image - */ - function get_image_url() { - return $this->_image_url; - } - - /** - * Return the image's error message - * - * @return string The image's error message - */ - function get_image_msg() { - return $this->_image_msg; - } - -} diff --git a/library/vendor/dompdf/include/image_frame_reflower.cls.php b/library/vendor/dompdf/include/image_frame_reflower.cls.php deleted file mode 100644 index c938bd075..000000000 --- a/library/vendor/dompdf/include/image_frame_reflower.cls.php +++ /dev/null @@ -1,186 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Image reflower class - * - * @access private - * @package dompdf - */ -class Image_Frame_Reflower extends Frame_Reflower { - - function __construct(Image_Frame_Decorator $frame) { - parent::__construct($frame); - } - - function reflow(Block_Frame_Decorator $block = null) { - $this->_frame->position(); - - //FLOAT - //$frame = $this->_frame; - //$page = $frame->get_root(); - - //$enable_css_float = $this->get_dompdf()->get_option("enable_css_float"); - //if ($enable_css_float && $frame->get_style()->float !== "none" ) { - // $page->add_floating_frame($this); - //} - // Set the frame's width - $this->get_min_max_width(); - - if ( $block ) { - $block->add_frame_to_line($this->_frame); - } - } - - function get_min_max_width() { - if (DEBUGPNG) { - // Determine the image's size. Time consuming. Only when really needed? - list($img_width, $img_height) = dompdf_getimagesize($this->_frame->get_image_url(), $this->get_dompdf()->get_http_context()); - print "get_min_max_width() ". - $this->_frame->get_style()->width.' '. - $this->_frame->get_style()->height.';'. - $this->_frame->get_parent()->get_style()->width." ". - $this->_frame->get_parent()->get_style()->height.";". - $this->_frame->get_parent()->get_parent()->get_style()->width.' '. - $this->_frame->get_parent()->get_parent()->get_style()->height.';'. - $img_width. ' '. - $img_height.'|' ; - } - - $style = $this->_frame->get_style(); - - $width_forced = true; - $height_forced = true; - - //own style auto or invalid value: use natural size in px - //own style value: ignore suffix text including unit, use given number as px - //own style %: walk up parent chain until found available space in pt; fill available space - // - //special ignored unit: e.g. 10ex: e treated as exponent; x ignored; 10e completely invalid ->like auto - - $width = ($style->width > 0 ? $style->width : 0); - if ( is_percent($width) ) { - $t = 0.0; - for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) { - $f_style = $f->get_style(); - $t = $f_style->length_in_pt($f_style->width); - if ($t != 0) { - break; - } - } - $width = ((float)rtrim($width,"%") * $t)/100; //maybe 0 - } elseif ( !mb_strpos($width, 'pt') ) { - // Don't set image original size if "%" branch was 0 or size not given. - // Otherwise aspect changed on %/auto combination for width/height - // Resample according to px per inch - // See also List_Bullet_Image_Frame_Decorator::__construct - $width = $style->length_in_pt($width); - } - - $height = ($style->height > 0 ? $style->height : 0); - if ( is_percent($height) ) { - $t = 0.0; - for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) { - $f_style = $f->get_style(); - $t = $f_style->length_in_pt($f_style->height); - if ($t != 0) { - break; - } - } - $height = ((float)rtrim($height,"%") * $t)/100; //maybe 0 - } elseif ( !mb_strpos($height, 'pt') ) { - // Don't set image original size if "%" branch was 0 or size not given. - // Otherwise aspect changed on %/auto combination for width/height - // Resample according to px per inch - // See also List_Bullet_Image_Frame_Decorator::__construct - $height = $style->length_in_pt($height); - } - - if ($width == 0 || $height == 0) { - // Determine the image's size. Time consuming. Only when really needed! - list($img_width, $img_height) = dompdf_getimagesize($this->_frame->get_image_url(), $this->get_dompdf()->get_http_context()); - - // don't treat 0 as error. Can be downscaled or can be catched elsewhere if image not readable. - // Resample according to px per inch - // See also List_Bullet_Image_Frame_Decorator::__construct - if ($width == 0 && $height == 0) { - $dpi = $this->_frame->get_dompdf()->get_option("dpi"); - $width = (float)($img_width * 72) / $dpi; - $height = (float)($img_height * 72) / $dpi; - $width_forced = false; - $height_forced = false; - } elseif ($height == 0 && $width != 0) { - $height_forced = false; - $height = ($width / $img_width) * $img_height; //keep aspect ratio - } elseif ($width == 0 && $height != 0) { - $width_forced = false; - $width = ($height / $img_height) * $img_width; //keep aspect ratio - } - } - - // Handle min/max width/height - if ( $style->min_width !== "none" || - $style->max_width !== "none" || - $style->min_height !== "none" || - $style->max_height !== "none" ) { - - list(/*$x*/, /*$y*/, $w, $h) = $this->_frame->get_containing_block(); - - $min_width = $style->length_in_pt($style->min_width, $w); - $max_width = $style->length_in_pt($style->max_width, $w); - $min_height = $style->length_in_pt($style->min_height, $h); - $max_height = $style->length_in_pt($style->max_height, $h); - - if ( $max_width !== "none" && $width > $max_width ) { - if ( !$height_forced ) { - $height *= $max_width / $width; - } - - $width = $max_width; - } - - if ( $min_width !== "none" && $width < $min_width ) { - if ( !$height_forced ) { - $height *= $min_width / $width; - } - - $width = $min_width; - } - - if ( $max_height !== "none" && $height > $max_height ) { - if ( !$width_forced ) { - $width *= $max_height / $height; - } - - $height = $max_height; - } - - if ( $min_height !== "none" && $height < $min_height ) { - if ( !$width_forced ) { - $width *= $min_height / $height; - } - - $height = $min_height; - } - } - - if (DEBUGPNG) print $width.' '.$height.';'; - - $style->width = $width . "pt"; - $style->height = $height . "pt"; - - $style->min_width = "none"; - $style->max_width = "none"; - $style->min_height = "none"; - $style->max_height = "none"; - - return array( $width, $width, "min" => $width, "max" => $width); - - } -} diff --git a/library/vendor/dompdf/include/image_renderer.cls.php b/library/vendor/dompdf/include/image_renderer.cls.php deleted file mode 100644 index 561b70153..000000000 --- a/library/vendor/dompdf/include/image_renderer.cls.php +++ /dev/null @@ -1,119 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Image renderer - * - * @access private - * @package dompdf - */ -class Image_Renderer extends Block_Renderer { - - function render(Frame $frame) { - // Render background & borders - $style = $frame->get_style(); - $cb = $frame->get_containing_block(); - list($x, $y, $w, $h) = $frame->get_border_box(); - - $this->_set_opacity( $frame->get_opacity( $style->opacity ) ); - - list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h); - - $has_border_radius = $tl + $tr + $br + $bl > 0; - - if ( $has_border_radius ) { - $this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl ); - } - - if ( ($bg = $style->background_color) !== "transparent" ) { - $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg); - } - - if ( ($url = $style->background_image) && $url !== "none" ) { - $this->_background_image($url, $x, $y, $w, $h, $style); - } - - if ( $has_border_radius ) { - $this->_canvas->clipping_end(); - } - - $this->_render_border($frame); - $this->_render_outline($frame); - - list($x, $y) = $frame->get_padding_box(); - - $x += $style->length_in_pt($style->padding_left, $cb["w"]); - $y += $style->length_in_pt($style->padding_top, $cb["h"]); - - $w = $style->length_in_pt($style->width, $cb["w"]); - $h = $style->length_in_pt($style->height, $cb["h"]); - - if ( $has_border_radius ) { - list($wt, $wr, $wb, $wl) = array( - $style->border_top_width, - $style->border_right_width, - $style->border_bottom_width, - $style->border_left_width, - ); - - // we have to get the "inner" radius - if ( $tl > 0 ) { - $tl -= ($wt + $wl) / 2; - } - if ( $tr > 0 ) { - $tr -= ($wt + $wr) / 2; - } - if ( $br > 0 ) { - $br -= ($wb + $wr) / 2; - } - if ( $bl > 0 ) { - $bl -= ($wb + $wl) / 2; - } - - $this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl ); - } - - $src = $frame->get_image_url(); - $alt = null; - - if ( Image_Cache::is_broken($src) && - $alt = $frame->get_node()->getAttribute("alt") ) { - $font = $style->font_family; - $size = $style->font_size; - $spacing = $style->word_spacing; - $this->_canvas->text($x, $y, $alt, - $font, $size, - $style->color, $spacing); - } - else { - $this->_canvas->image( $src, $x, $y, $w, $h, $style->image_resolution); - } - - if ( $has_border_radius ) { - $this->_canvas->clipping_end(); - } - - if ( $msg = $frame->get_image_msg() ) { - $parts = preg_split("/\s*\n\s*/", $msg); - $height = 10; - $_y = $alt ? $y+$h-count($parts)*$height : $y; - - foreach($parts as $i => $_part) { - $this->_canvas->text($x, $_y + $i*$height, $_part, "times", $height*0.8, array(0.5, 0.5, 0.5)); - } - } - - if (DEBUG_LAYOUT && DEBUG_LAYOUT_BLOCKS) { - $this->_debug_layout($frame->get_border_box(), "blue"); - if (DEBUG_LAYOUT_PADDINGBOX) { - $this->_debug_layout($frame->get_padding_box(), "blue", array(0.5, 0.5)); - } - } - } -} diff --git a/library/vendor/dompdf/include/inline_frame_decorator.cls.php b/library/vendor/dompdf/include/inline_frame_decorator.cls.php deleted file mode 100644 index ce79bab08..000000000 --- a/library/vendor/dompdf/include/inline_frame_decorator.cls.php +++ /dev/null @@ -1,74 +0,0 @@ - - * @author Helmut Tischer - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Decorates frames for inline layout - * - * @access private - * @package dompdf - */ -class Inline_Frame_Decorator extends Frame_Decorator { - - function __construct(Frame $frame, DOMPDF $dompdf) { parent::__construct($frame, $dompdf); } - - function split(Frame $frame = null, $force_pagebreak = false) { - - if ( is_null($frame) ) { - $this->get_parent()->split($this, $force_pagebreak); - return; - } - - if ( $frame->get_parent() !== $this ) - throw new DOMPDF_Exception("Unable to split: frame is not a child of this one."); - - $split = $this->copy( $this->_frame->get_node()->cloneNode() ); - $this->get_parent()->insert_child_after($split, $this); - - // Unset the current node's right style properties - $style = $this->_frame->get_style(); - $style->margin_right = 0; - $style->padding_right = 0; - $style->border_right_width = 0; - - // Unset the split node's left style properties since we don't want them - // to propagate - $style = $split->get_style(); - $style->margin_left = 0; - $style->padding_left = 0; - $style->border_left_width = 0; - - //On continuation of inline element on next line, - //don't repeat non-vertically repeatble background images - //See e.g. in testcase image_variants, long desriptions - if ( ($url = $style->background_image) && $url !== "none" - && ($repeat = $style->background_repeat) && $repeat !== "repeat" && $repeat !== "repeat-y" - ) { - $style->background_image = "none"; - } - - // Add $frame and all following siblings to the new split node - $iter = $frame; - while ($iter) { - $frame = $iter; - $iter = $iter->get_next_sibling(); - $frame->reset(); - $split->append_child($frame); - } - - $page_breaks = array("always", "left", "right"); - $frame_style = $frame->get_style(); - if( $force_pagebreak || - in_array($frame_style->page_break_before, $page_breaks) || - in_array($frame_style->page_break_after, $page_breaks) ) { - - $this->get_parent()->split($split, true); - } - } - -} diff --git a/library/vendor/dompdf/include/inline_frame_reflower.cls.php b/library/vendor/dompdf/include/inline_frame_reflower.cls.php deleted file mode 100644 index 049b8e586..000000000 --- a/library/vendor/dompdf/include/inline_frame_reflower.cls.php +++ /dev/null @@ -1,66 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Reflows inline frames - * - * @access private - * @package dompdf - */ -class Inline_Frame_Reflower extends Frame_Reflower { - - function __construct(Frame $frame) { parent::__construct($frame); } - - //........................................................................ - - function reflow(Block_Frame_Decorator $block = null) { - $frame = $this->_frame; - - // Check if a page break is forced - $page = $frame->get_root(); - $page->check_forced_page_break($frame); - - if ( $page->is_full() ) - return; - - $style = $frame->get_style(); - - // Generated content - $this->_set_content(); - - $frame->position(); - - $cb = $frame->get_containing_block(); - - // Add our margin, padding & border to the first and last children - if ( ($f = $frame->get_first_child()) && $f instanceof Text_Frame_Decorator ) { - $f_style = $f->get_style(); - $f_style->margin_left = $style->margin_left; - $f_style->padding_left = $style->padding_left; - $f_style->border_left = $style->border_left; - } - - if ( ($l = $frame->get_last_child()) && $l instanceof Text_Frame_Decorator ) { - $l_style = $l->get_style(); - $l_style->margin_right = $style->margin_right; - $l_style->padding_right = $style->padding_right; - $l_style->border_right = $style->border_right; - } - - if ( $block ) { - $block->add_frame_to_line($this->_frame); - } - - // Set the containing blocks and reflow each child. The containing - // block is not changed by line boxes. - foreach ( $frame->get_children() as $child ) { - $child->set_containing_block($cb); - $child->reflow($block); - } - } -} diff --git a/library/vendor/dompdf/include/inline_positioner.cls.php b/library/vendor/dompdf/include/inline_positioner.cls.php deleted file mode 100644 index 1694ce8e2..000000000 --- a/library/vendor/dompdf/include/inline_positioner.cls.php +++ /dev/null @@ -1,70 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Positions inline frames - * - * @access private - * @package dompdf - */ -class Inline_Positioner extends Positioner { - - function __construct(Frame_Decorator $frame) { parent::__construct($frame); } - - //........................................................................ - - function position() { - /** - * Find our nearest block level parent and access its lines property. - * @var Block_Frame_Decorator - */ - $p = $this->_frame->find_block_parent(); - - // Debugging code: - -// pre_r("\nPositioning:"); -// pre_r("Me: " . $this->_frame->get_node()->nodeName . " (" . spl_object_hash($this->_frame->get_node()) . ")"); -// pre_r("Parent: " . $p->get_node()->nodeName . " (" . spl_object_hash($p->get_node()) . ")"); - - // End debugging - - if ( !$p ) - throw new DOMPDF_Exception("No block-level parent found. Not good."); - - $f = $this->_frame; - - $cb = $f->get_containing_block(); - $line = $p->get_current_line_box(); - - // Skip the page break if in a fixed position element - $is_fixed = false; - while($f = $f->get_parent()) { - if($f->get_style()->position === "fixed") { - $is_fixed = true; - break; - } - } - - $f = $this->_frame; - - if ( !$is_fixed && $f->get_parent() && - $f->get_parent() instanceof Inline_Frame_Decorator && - $f->is_text_node() ) { - - $min_max = $f->get_reflower()->get_min_max_width(); - - // If the frame doesn't fit in the current line, a line break occurs - if ( $min_max["min"] > ($cb["w"] - $line->left - $line->w - $line->right) ) { - $p->add_line(); - } - } - - $f->set_position($cb["x"] + $line->w, $line->y); - - } -} diff --git a/library/vendor/dompdf/include/inline_renderer.cls.php b/library/vendor/dompdf/include/inline_renderer.cls.php deleted file mode 100644 index 7a8ff51c3..000000000 --- a/library/vendor/dompdf/include/inline_renderer.cls.php +++ /dev/null @@ -1,190 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Renders inline frames - * - * @access private - * @package dompdf - */ -class Inline_Renderer extends Abstract_Renderer { - - //........................................................................ - - function render(Frame $frame) { - $style = $frame->get_style(); - - if ( !$frame->get_first_child() ) - return; // No children, no service - - // Draw the left border if applicable - $bp = $style->get_border_properties(); - $widths = array($style->length_in_pt($bp["top"]["width"]), - $style->length_in_pt($bp["right"]["width"]), - $style->length_in_pt($bp["bottom"]["width"]), - $style->length_in_pt($bp["left"]["width"])); - - // Draw the background & border behind each child. To do this we need - // to figure out just how much space each child takes: - list($x, $y) = $frame->get_first_child()->get_position(); - $w = null; - $h = 0; -// $x += $widths[3]; -// $y += $widths[0]; - - $this->_set_opacity( $frame->get_opacity( $style->opacity ) ); - - $first_row = true; - - foreach ($frame->get_children() as $child) { - list($child_x, $child_y, $child_w, $child_h) = $child->get_padding_box(); - - if ( !is_null($w) && $child_x < $x + $w ) { - //This branch seems to be supposed to being called on the first part - //of an inline html element, and the part after the if clause for the - //parts after a line break. - //But because $w initially mostly is 0, and gets updated only on the next - //round, this seem to be never executed and the common close always. - - // The next child is on another line. Draw the background & - // borders on this line. - - // Background: - if ( ($bg = $style->background_color) !== "transparent" ) - $this->_canvas->filled_rectangle( $x, $y, $w, $h, $bg); - - if ( ($url = $style->background_image) && $url !== "none" ) { - $this->_background_image($url, $x, $y, $w, $h, $style); - } - - // If this is the first row, draw the left border - if ( $first_row ) { - - if ( $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $bp["left"]["width"] > 0 ) { - $method = "_border_" . $bp["left"]["style"]; - $this->$method($x, $y, $h + $widths[0] + $widths[2], $bp["left"]["color"], $widths, "left"); - } - $first_row = false; - } - - // Draw the top & bottom borders - if ( $bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $bp["top"]["width"] > 0 ) { - $method = "_border_" . $bp["top"]["style"]; - $this->$method($x, $y, $w + $widths[1] + $widths[3], $bp["top"]["color"], $widths, "top"); - } - - if ( $bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $bp["bottom"]["width"] > 0 ) { - $method = "_border_" . $bp["bottom"]["style"]; - $this->$method($x, $y + $h + $widths[0] + $widths[2], $w + $widths[1] + $widths[3], $bp["bottom"]["color"], $widths, "bottom"); - } - - // Handle anchors & links - $link_node = null; - if ( $frame->get_node()->nodeName === "a" ) { - $link_node = $frame->get_node(); - } - else if ( $frame->get_parent()->get_node()->nodeName === "a" ){ - $link_node = $frame->get_parent()->get_node(); - } - - if ( $link_node && $href = $link_node->getAttribute("href") ) { - $this->_canvas->add_link($href, $x, $y, $w, $h); - } - - $x = $child_x; - $y = $child_y; - $w = $child_w; - $h = $child_h; - continue; - } - - if ( is_null($w) ) - $w = $child_w; - else - $w += $child_w; - - $h = max($h, $child_h); - - if (DEBUG_LAYOUT && DEBUG_LAYOUT_INLINE) { - $this->_debug_layout($child->get_border_box(), "blue"); - if (DEBUG_LAYOUT_PADDINGBOX) { - $this->_debug_layout($child->get_padding_box(), "blue", array(0.5, 0.5)); - } - } - } - - - // Handle the last child - if ( ($bg = $style->background_color) !== "transparent" ) - $this->_canvas->filled_rectangle( $x + $widths[3], $y + $widths[0], $w, $h, $bg); - - //On continuation lines (after line break) of inline elements, the style got copied. - //But a non repeatable background image should not be repeated on the next line. - //But removing the background image above has never an effect, and removing it below - //removes it always, even on the initial line. - //Need to handle it elsewhere, e.g. on certain ...clone()... usages. - // Repeat not given: default is Style::__construct - // ... && (!($repeat = $style->background_repeat) || $repeat === "repeat" ... - //different position? $this->_background_image($url, $x, $y, $w, $h, $style); - if ( ($url = $style->background_image) && $url !== "none" ) - $this->_background_image($url, $x + $widths[3], $y + $widths[0], $w, $h, $style); - - // Add the border widths - $w += $widths[1] + $widths[3]; - $h += $widths[0] + $widths[2]; - - // make sure the border and background start inside the left margin - $left_margin = $style->length_in_pt($style->margin_left); - $x += $left_margin; - - // If this is the first row, draw the left border too - if ( $first_row && $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $widths[3] > 0 ) { - $method = "_border_" . $bp["left"]["style"]; - $this->$method($x, $y, $h, $bp["left"]["color"], $widths, "left"); - } - - // Draw the top & bottom borders - if ( $bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $widths[0] > 0 ) { - $method = "_border_" . $bp["top"]["style"]; - $this->$method($x, $y, $w, $bp["top"]["color"], $widths, "top"); - } - - if ( $bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $widths[2] > 0 ) { - $method = "_border_" . $bp["bottom"]["style"]; - $this->$method($x, $y + $h, $w, $bp["bottom"]["color"], $widths, "bottom"); - } - - // pre_var_dump(get_class($frame->get_next_sibling())); - // $last_row = get_class($frame->get_next_sibling()) !== 'Inline_Frame_Decorator'; - // Draw the right border if this is the last row - if ( $bp["right"]["style"] !== "none" && $bp["right"]["color"] !== "transparent" && $widths[1] > 0 ) { - $method = "_border_" . $bp["right"]["style"]; - $this->$method($x + $w, $y, $h, $bp["right"]["color"], $widths, "right"); - } - - // Only two levels of links frames - $link_node = null; - if ( $frame->get_node()->nodeName === "a" ) { - $link_node = $frame->get_node(); - - if ( ($name = $link_node->getAttribute("name")) || ($name = $link_node->getAttribute("id")) ) { - $this->_canvas->add_named_dest($name); - } - } - - if ( $frame->get_parent() && $frame->get_parent()->get_node()->nodeName === "a" ){ - $link_node = $frame->get_parent()->get_node(); - } - - // Handle anchors & links - if ( $link_node ) { - if ( $href = $link_node->getAttribute("href") ) - $this->_canvas->add_link($href, $x, $y, $w, $h); - } - } -} diff --git a/library/vendor/dompdf/include/javascript_embedder.cls.php b/library/vendor/dompdf/include/javascript_embedder.cls.php deleted file mode 100644 index 92c244b2d..000000000 --- a/library/vendor/dompdf/include/javascript_embedder.cls.php +++ /dev/null @@ -1,37 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Embeds Javascript into the PDF document - * - * @access private - * @package dompdf - */ -class Javascript_Embedder { - - /** - * @var DOMPDF - */ - protected $_dompdf; - - function __construct(DOMPDF $dompdf) { - $this->_dompdf = $dompdf; - } - - function insert($script) { - $this->_dompdf->get_canvas()->javascript($script); - } - - function render(Frame $frame) { - if ( !$this->_dompdf->get_option("enable_javascript") ) { - return; - } - - $this->insert($frame->get_node()->nodeValue); - } -} diff --git a/library/vendor/dompdf/include/line_box.cls.php b/library/vendor/dompdf/include/line_box.cls.php deleted file mode 100644 index 352359729..000000000 --- a/library/vendor/dompdf/include/line_box.cls.php +++ /dev/null @@ -1,252 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * The line box class - * - * This class represents a line box - * http://www.w3.org/TR/CSS2/visuren.html#line-box - * - * @access protected - * @package dompdf - */ -class Line_Box { - - /** - * @var Block_Frame_Decorator - */ - protected $_block_frame; - - /** - * @var Frame[] - */ - protected $_frames = array(); - - /** - * @var integer - */ - public $wc = 0; - - /** - * @var float - */ - public $y = null; - - /** - * @var float - */ - public $w = 0.0; - - /** - * @var float - */ - public $h = 0.0; - - /** - * @var float - */ - public $left = 0.0; - - /** - * @var float - */ - public $right = 0.0; - - /** - * @var Frame - */ - public $tallest_frame = null; - - /** - * @var bool[] - */ - public $floating_blocks = array(); - - /** - * @var bool - */ - public $br = false; - - /** - * Class constructor - * - * @param Block_Frame_Decorator $frame the Block_Frame_Decorator containing this line - */ - function __construct(Block_Frame_Decorator $frame, $y = 0) { - $this->_block_frame = $frame; - $this->_frames = array(); - $this->y = $y; - - $this->get_float_offsets(); - } - - /** - * Returns the floating elements inside the first floating parent - * - * @param Page_Frame_Decorator $root - * - * @return Frame[] - */ - function get_floats_inside(Page_Frame_Decorator $root) { - $floating_frames = $root->get_floating_frames(); - - if ( count($floating_frames) == 0 ) { - return $floating_frames; - } - - // Find nearest floating element - $p = $this->_block_frame; - while( $p->get_style()->float === "none" ) { - $parent = $p->get_parent(); - - if ( !$parent ) { - break; - } - - $p = $parent; - } - - if ( $p == $root ) { - return $floating_frames; - } - - $parent = $p; - - $childs = array(); - - foreach ($floating_frames as $_floating) { - $p = $_floating->get_parent(); - - while (($p = $p->get_parent()) && $p !== $parent); - - if ( $p ) { - $childs[] = $p; - } - } - - return $childs; - } - - function get_float_offsets() { - $enable_css_float = $this->_block_frame->get_dompdf()->get_option("enable_css_float"); - if ( !$enable_css_float ) { - return; - } - - static $anti_infinite_loop = 500; // FIXME smelly hack - - $reflower = $this->_block_frame->get_reflower(); - - if ( !$reflower ) { - return; - } - - $cb_w = null; - - $block = $this->_block_frame; - $root = $block->get_root(); - - if ( !$root ) { - return; - } - - $floating_frames = $this->get_floats_inside($root); - - foreach ( $floating_frames as $child_key => $floating_frame ) { - $id = $floating_frame->get_id(); - - if ( isset($this->floating_blocks[$id]) ) { - continue; - } - - $floating_style = $floating_frame->get_style(); - $float = $floating_style->float; - - $floating_width = $floating_frame->get_margin_width(); - - if (!$cb_w) { - $cb_w = $floating_frame->get_containing_block("w"); - } - - $line_w = $this->get_width(); - - if ( !$floating_frame->_float_next_line && ($cb_w <= $line_w + $floating_width) && ($cb_w > $line_w) ) { - $floating_frame->_float_next_line = true; - continue; - } - - // If the child is still shifted by the floating element - if ( $anti_infinite_loop-- > 0 && - $floating_frame->get_position("y") + $floating_frame->get_margin_height() > $this->y && - $block->get_position("x") + $block->get_margin_width() > $floating_frame->get_position("x") - ) { - if ( $float === "left" ) - $this->left += $floating_width; - else - $this->right += $floating_width; - - $this->floating_blocks[$id] = true; - } - - // else, the floating element won't shift anymore - else { - $root->remove_floating_frame($child_key); - } - } - } - - /** - * @return float - */ - function get_width(){ - return $this->left + $this->w + $this->right; - } - - /** - * @return Block_Frame_Decorator - */ - function get_block_frame() { - return $this->_block_frame; - } - - /** - * @return Frame[] - */ - function &get_frames() { - return $this->_frames; - } - - /** - * @param Frame $frame - */ - function add_frame(Frame $frame) { - $this->_frames[] = $frame; - } - - function __toString(){ - $props = array("wc", "y", "w", "h", "left", "right", "br"); - $s = ""; - foreach($props as $prop) { - $s .= "$prop: ".$this->$prop."\n"; - } - $s .= count($this->_frames)." frames\n"; - return $s; - } - /*function __get($prop) { - if (!isset($this->{"_$prop"})) return; - return $this->{"_$prop"}; - }*/ -} - -/* -class LineBoxList implements Iterator { - private $_p = 0; - private $_lines = array(); - -} -*/ diff --git a/library/vendor/dompdf/include/list_bullet_frame_decorator.cls.php b/library/vendor/dompdf/include/list_bullet_frame_decorator.cls.php deleted file mode 100644 index a661dc050..000000000 --- a/library/vendor/dompdf/include/list_bullet_frame_decorator.cls.php +++ /dev/null @@ -1,65 +0,0 @@ - - * @author Helmut Tischer - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Decorates frames for list bullet rendering - * - * @access private - * @package dompdf - */ -class List_Bullet_Frame_Decorator extends Frame_Decorator { - - const BULLET_PADDING = 1; // Distance from bullet to text in pt - // As fraction of font size (including descent). See also DECO_THICKNESS. - const BULLET_THICKNESS = 0.04; // Thickness of bullet outline. Screen: 0.08, print: better less, e.g. 0.04 - const BULLET_DESCENT = 0.3; //descent of font below baseline. Todo: Guessed for now. - const BULLET_SIZE = 0.35; // bullet diameter. For now 0.5 of font_size without descent. - - static $BULLET_TYPES = array("disc", "circle", "square"); - - //........................................................................ - - function __construct(Frame $frame, DOMPDF $dompdf) { - parent::__construct($frame, $dompdf); - } - - function get_margin_width() { - $style = $this->_frame->get_style(); - - // Small hack to prevent extra indenting of list text on list_style_position === "inside" - // and on suppressed bullet - if ( $style->list_style_position === "outside" || - $style->list_style_type === "none" ) { - return 0; - } - - return $style->get_font_size() * self::BULLET_SIZE + 2 * self::BULLET_PADDING; - } - - //hits only on "inset" lists items, to increase height of box - function get_margin_height() { - $style = $this->_frame->get_style(); - - if ( $style->list_style_type === "none" ) { - return 0; - } - - return $style->get_font_size() * self::BULLET_SIZE + 2 * self::BULLET_PADDING; - } - - function get_width() { - return $this->get_margin_height(); - } - - function get_height() { - return $this->get_margin_height(); - } - - //........................................................................ -} diff --git a/library/vendor/dompdf/include/list_bullet_frame_reflower.cls.php b/library/vendor/dompdf/include/list_bullet_frame_reflower.cls.php deleted file mode 100644 index 283056f00..000000000 --- a/library/vendor/dompdf/include/list_bullet_frame_reflower.cls.php +++ /dev/null @@ -1,33 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Reflows list bullets - * - * @access private - * @package dompdf - */ -class List_Bullet_Frame_Reflower extends Frame_Reflower { - - function __construct(Frame_Decorator $frame) { parent::__construct($frame); } - - //........................................................................ - - function reflow(Block_Frame_Decorator $block = null) { - $style = $this->_frame->get_style(); - - $style->width = $this->_frame->get_width(); - $this->_frame->position(); - - if ( $style->list_style_position === "inside" ) { - $p = $this->_frame->find_block_parent(); - $p->add_frame_to_line($this->_frame); - } - - } -} diff --git a/library/vendor/dompdf/include/list_bullet_image_frame_decorator.cls.php b/library/vendor/dompdf/include/list_bullet_image_frame_decorator.cls.php deleted file mode 100644 index 6cfb546cb..000000000 --- a/library/vendor/dompdf/include/list_bullet_image_frame_decorator.cls.php +++ /dev/null @@ -1,143 +0,0 @@ - - * @author Helmut Tischer - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Decorates frames for list bullets with custom images - * - * @access private - * @package dompdf - */ -class List_Bullet_Image_Frame_Decorator extends Frame_Decorator { - - /** - * The underlying image frame - * - * @var Image_Frame_Decorator - */ - protected $_img; - - /** - * The image's width in pixels - * - * @var int - */ - protected $_width; - - /** - * The image's height in pixels - * - * @var int - */ - protected $_height; - - /** - * Class constructor - * - * @param Frame $frame the bullet frame to decorate - * @param DOMPDF $dompdf the document's dompdf object - */ - function __construct(Frame $frame, DOMPDF $dompdf) { - $style = $frame->get_style(); - $url = $style->list_style_image; - $frame->get_node()->setAttribute("src", $url); - $this->_img = new Image_Frame_Decorator($frame, $dompdf); - parent::__construct($this->_img, $dompdf); - list($width, $height) = dompdf_getimagesize($this->_img->get_image_url(), $dompdf->get_http_context()); - - // Resample the bullet image to be consistent with 'auto' sized images - // See also Image_Frame_Reflower::get_min_max_width - // Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary. - $dpi = $this->_dompdf->get_option("dpi"); - $this->_width = ((float)rtrim($width, "px") * 72) / $dpi; - $this->_height = ((float)rtrim($height, "px") * 72) / $dpi; - - //If an image is taller as the containing block/box, the box should be extended. - //Neighbour elements are overwriting the overlapping image areas. - //Todo: Where can the box size be extended? - //Code below has no effect. - //See block_frame_reflower _calculate_restricted_height - //See generated_frame_reflower, Dompdf:render() "list-item", "-dompdf-list-bullet"S. - //Leave for now - //if ($style->min_height < $this->_height ) { - // $style->min_height = $this->_height; - //} - //$style->height = "auto"; - } - - /** - * Return the bullet's width - * - * @return int - */ - function get_width() { - //ignore image width, use same width as on predefined bullet List_Bullet_Frame_Decorator - //for proper alignment of bullet image and text. Allow image to not fitting on left border. - //This controls the distance between bullet image and text - //return $this->_width; - return $this->_frame->get_style()->get_font_size()*List_Bullet_Frame_Decorator::BULLET_SIZE + - 2 * List_Bullet_Frame_Decorator::BULLET_PADDING; - } - - /** - * Return the bullet's height - * - * @return int - */ - function get_height() { - //based on image height - return $this->_height; - } - - /** - * Override get_margin_width - * - * @return int - */ - function get_margin_width() { - //ignore image width, use same width as on predefined bullet List_Bullet_Frame_Decorator - //for proper alignment of bullet image and text. Allow image to not fitting on left border. - //This controls the extra indentation of text to make room for the bullet image. - //Here use actual image size, not predefined bullet size - //return $this->_frame->get_style()->get_font_size()*List_Bullet_Frame_Decorator::BULLET_SIZE + - // 2 * List_Bullet_Frame_Decorator::BULLET_PADDING; - - // Small hack to prevent indenting of list text - // Image Might not exist, then position like on list_bullet_frame_decorator fallback to none. - if ( $this->_frame->get_style()->list_style_position === "outside" || - $this->_width == 0) - return 0; - //This aligns the "inside" image position with the text. - //The text starts to the right of the image. - //Between the image and the text there is an added margin of image width. - //Where this comes from is unknown. - //The corresponding List_Bullet_Frame_Decorator sets a smaller margin. bullet size? - return $this->_width + 2 * List_Bullet_Frame_Decorator::BULLET_PADDING; - } - - /** - * Override get_margin_height() - * - * @return int - */ - function get_margin_height() { - //Hits only on "inset" lists items, to increase height of box - //based on image height - return $this->_height + 2 * List_Bullet_Frame_Decorator::BULLET_PADDING; - } - - /** - * Return image url - * - * @return string - */ - function get_image_url() { - return $this->_img->get_image_url(); - } - -} diff --git a/library/vendor/dompdf/include/list_bullet_positioner.cls.php b/library/vendor/dompdf/include/list_bullet_positioner.cls.php deleted file mode 100644 index 7e89ae471..000000000 --- a/library/vendor/dompdf/include/list_bullet_positioner.cls.php +++ /dev/null @@ -1,73 +0,0 @@ - - * @author Helmut Tischer - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Positions list bullets - * - * @access private - * @package dompdf - */ -class List_Bullet_Positioner extends Positioner { - - function __construct(Frame_Decorator $frame) { parent::__construct($frame); } - - //........................................................................ - - function position() { - - // Bullets & friends are positioned an absolute distance to the left of - // the content edge of their parent element - $cb = $this->_frame->get_containing_block(); - - // Note: this differs from most frames in that we must position - // ourselves after determining our width - $x = $cb["x"] - $this->_frame->get_width(); - - $p = $this->_frame->find_block_parent(); - - $y = $p->get_current_line_box()->y; - - // This is a bit of a hack... - $n = $this->_frame->get_next_sibling(); - if ( $n ) { - $style = $n->get_style(); - $line_height = $style->length_in_pt($style->line_height, $style->get_font_size()); - $offset = $style->length_in_pt($line_height, $n->get_containing_block("h")) - $this->_frame->get_height(); - $y += $offset / 2; - } - - // Now the position is the left top of the block which should be marked with the bullet. - // We tried to find out the y of the start of the first text character within the block. - // But the top margin/padding does not fit, neither from this nor from the next sibling - // The "bit of a hack" above does not work also. - - // Instead let's position the bullet vertically centered to the block which should be marked. - // But for get_next_sibling() the get_containing_block is all zero, and for find_block_parent() - // the get_containing_block is paper width and the entire list as height. - - // if ($p) { - // //$cb = $n->get_containing_block(); - // $cb = $p->get_containing_block(); - // $y += $cb["h"]/2; - // print 'cb:'.$cb["x"].':'.$cb["y"].':'.$cb["w"].':'.$cb["h"].':'; - // } - - // Todo: - // For now give up on the above. Use Guesswork with font y-pos in the middle of the line spacing - - /*$style = $p->get_style(); - $font_size = $style->get_font_size(); - $line_height = $style->length_in_pt($style->line_height, $font_size); - $y += ($line_height - $font_size) / 2; */ - - //Position is x-end y-top of character position of the bullet. - $this->_frame->set_position($x, $y); - - } -} diff --git a/library/vendor/dompdf/include/list_bullet_renderer.cls.php b/library/vendor/dompdf/include/list_bullet_renderer.cls.php deleted file mode 100644 index be4cde2bd..000000000 --- a/library/vendor/dompdf/include/list_bullet_renderer.cls.php +++ /dev/null @@ -1,236 +0,0 @@ - - * @author Helmut Tischer - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Renders list bullets - * - * @access private - * @package dompdf - */ -class List_Bullet_Renderer extends Abstract_Renderer { - static function get_counter_chars($type) { - static $cache = array(); - - if ( isset($cache[$type]) ) { - return $cache[$type]; - } - - $uppercase = false; - $text = ""; - - switch ($type) { - case "decimal-leading-zero": - case "decimal": - case "1": - return "0123456789"; - - case "upper-alpha": - case "upper-latin": - case "A": - $uppercase = true; - case "lower-alpha": - case "lower-latin": - case "a": - $text = "abcdefghijklmnopqrstuvwxyz"; - break; - - case "upper-roman": - case "I": - $uppercase = true; - case "lower-roman": - case "i": - $text = "ivxlcdm"; - break; - - case "lower-greek": - for($i = 0; $i < 24; $i++) { - $text .= unichr($i+944); - } - break; - } - - if ( $uppercase ) { - $text = strtoupper($text); - } - - return $cache[$type] = "$text."; - } - - /** - * @param integer $n - * @param string $type - * @param integer $pad - * - * @return string - */ - private function make_counter($n, $type, $pad = null){ - $n = intval($n); - $text = ""; - $uppercase = false; - - switch ($type) { - case "decimal-leading-zero": - case "decimal": - case "1": - if ($pad) - $text = str_pad($n, $pad, "0", STR_PAD_LEFT); - else - $text = $n; - break; - - case "upper-alpha": - case "upper-latin": - case "A": - $uppercase = true; - case "lower-alpha": - case "lower-latin": - case "a": - $text = chr( ($n % 26) + ord('a') - 1); - break; - - case "upper-roman": - case "I": - $uppercase = true; - case "lower-roman": - case "i": - $text = dec2roman($n); - break; - - case "lower-greek": - $text = unichr($n + 944); - break; - } - - if ( $uppercase ) { - $text = strtoupper($text); - } - - return "$text."; - } - - function render(Frame $frame) { - $style = $frame->get_style(); - $font_size = $style->get_font_size(); - $line_height = $style->length_in_pt($style->line_height, $frame->get_containing_block("w")); - - $this->_set_opacity( $frame->get_opacity( $style->opacity ) ); - - $li = $frame->get_parent(); - - // Don't render bullets twice if if was split - if ($li->_splitted) { - return; - } - - // Handle list-style-image - // If list style image is requested but missing, fall back to predefined types - if ( $style->list_style_image !== "none" && - !Image_Cache::is_broken($img = $frame->get_image_url())) { - - list($x,$y) = $frame->get_position(); - - //For expected size and aspect, instead of box size, use image natural size scaled to DPI. - // Resample the bullet image to be consistent with 'auto' sized images - // See also Image_Frame_Reflower::get_min_max_width - // Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary. - //$w = $frame->get_width(); - //$h = $frame->get_height(); - list($width, $height) = dompdf_getimagesize($img, $this->_dompdf->get_http_context()); - $dpi = $this->_dompdf->get_option("dpi"); - $w = ((float)rtrim($width, "px") * 72) / $dpi; - $h = ((float)rtrim($height, "px") * 72) / $dpi; - - $x -= $w; - $y -= ($line_height - $font_size)/2; //Reverse hinting of list_bullet_positioner - - $this->_canvas->image( $img, $x, $y, $w, $h); - - } else { - - $bullet_style = $style->list_style_type; - - $fill = false; - - switch ($bullet_style) { - - default: - case "disc": - $fill = true; - - case "circle": - list($x,$y) = $frame->get_position(); - $r = ($font_size*(List_Bullet_Frame_Decorator::BULLET_SIZE /*-List_Bullet_Frame_Decorator::BULLET_THICKNESS*/ ))/2; - $x -= $font_size*(List_Bullet_Frame_Decorator::BULLET_SIZE/2); - $y += ($font_size*(1-List_Bullet_Frame_Decorator::BULLET_DESCENT))/2; - $o = $font_size*List_Bullet_Frame_Decorator::BULLET_THICKNESS; - $this->_canvas->circle($x, $y, $r, $style->color, $o, null, $fill); - break; - - case "square": - list($x, $y) = $frame->get_position(); - $w = $font_size*List_Bullet_Frame_Decorator::BULLET_SIZE; - $x -= $w; - $y += ($font_size*(1-List_Bullet_Frame_Decorator::BULLET_DESCENT-List_Bullet_Frame_Decorator::BULLET_SIZE))/2; - $this->_canvas->filled_rectangle($x, $y, $w, $w, $style->color); - break; - - case "decimal-leading-zero": - case "decimal": - case "lower-alpha": - case "lower-latin": - case "lower-roman": - case "lower-greek": - case "upper-alpha": - case "upper-latin": - case "upper-roman": - case "1": // HTML 4.0 compatibility - case "a": - case "i": - case "A": - case "I": - $pad = null; - if ( $bullet_style === "decimal-leading-zero" ) { - $pad = strlen($li->get_parent()->get_node()->getAttribute("dompdf-children-count")); - } - - $node = $frame->get_node(); - - if ( !$node->hasAttribute("dompdf-counter") ) { - return; - } - - $index = $node->getAttribute("dompdf-counter"); - $text = $this->make_counter($index, $bullet_style, $pad); - - if ( trim($text) == "" ) { - return; - } - - $spacing = 0; - $font_family = $style->font_family; - - $line = $li->get_containing_line(); - list($x, $y) = array($frame->get_position("x"), $line->y); - - $x -= Font_Metrics::get_text_width($text, $font_family, $font_size, $spacing); - - // Take line-height into account - $line_height = $style->line_height; - $y += ($line_height - $font_size) / 4; // FIXME I thought it should be 2, but 4 gives better results - - $this->_canvas->text($x, $y, $text, - $font_family, $font_size, - $style->color, $spacing); - - case "none": - break; - } - } - } -} diff --git a/library/vendor/dompdf/include/null_frame_decorator.cls.php b/library/vendor/dompdf/include/null_frame_decorator.cls.php deleted file mode 100644 index 15f806d0d..000000000 --- a/library/vendor/dompdf/include/null_frame_decorator.cls.php +++ /dev/null @@ -1,26 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Dummy decorator - * - * @access private - * @package dompdf - */ -class Null_Frame_Decorator extends Frame_Decorator { - - function __construct(Frame $frame, DOMPDF $dompdf) { - parent::__construct($frame, $dompdf); - $style = $this->_frame->get_style(); - $style->width = 0; - $style->height = 0; - $style->margin = 0; - $style->padding = 0; - } - -} diff --git a/library/vendor/dompdf/include/null_frame_reflower.cls.php b/library/vendor/dompdf/include/null_frame_reflower.cls.php deleted file mode 100644 index 389f1479d..000000000 --- a/library/vendor/dompdf/include/null_frame_reflower.cls.php +++ /dev/null @@ -1,21 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Dummy reflower - * - * @access private - * @package dompdf - */ -class Null_Frame_Reflower extends Frame_Reflower { - - function __construct(Frame $frame) { parent::__construct($frame); } - - function reflow(Block_Frame_Decorator $block = null) { return; } - -} diff --git a/library/vendor/dompdf/include/null_positioner.cls.php b/library/vendor/dompdf/include/null_positioner.cls.php deleted file mode 100644 index 97d4986dc..000000000 --- a/library/vendor/dompdf/include/null_positioner.cls.php +++ /dev/null @@ -1,23 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Dummy positioner - * - * @access private - * @package dompdf - */ -class Null_Positioner extends Positioner { - - function __construct(Frame_Decorator $frame) { - parent::__construct($frame); - } - - function position() { return; } - -} diff --git a/library/vendor/dompdf/include/page_cache.cls.php b/library/vendor/dompdf/include/page_cache.cls.php deleted file mode 100644 index 652da160a..000000000 --- a/library/vendor/dompdf/include/page_cache.cls.php +++ /dev/null @@ -1,126 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Caches individual rendered PDF pages - * - * Not totally implemented yet. Use at your own risk ;) - * - * @access private - * @package dompdf - * @static - */ -class Page_Cache { - - const DB_USER = "dompdf_page_cache"; - const DB_PASS = "some meaningful password"; - const DB_NAME = "dompdf_page_cache"; - - static private $__connection = null; - - static function init() { - if ( is_null(self::$__connection) ) { - $con_str = "host=" . DB_HOST . - " dbname=" . self::DB_NAME . - " user=" . self::DB_USER . - " password=" . self::DB_PASS; - - if ( !self::$__connection = pg_connect($con_str) ) - throw new Exception("Database connection failed."); - } - } - - function __construct() { throw new Exception("Can not create instance of Page_Class. Class is static."); } - - private static function __query($sql) { - if ( !($res = pg_query(self::$__connection, $sql)) ) - throw new Exception(pg_last_error(self::$__connection)); - return $res; - } - - static function store_page($id, $page_num, $data) { - $where = "WHERE id='" . pg_escape_string($id) . "' AND ". - "page_num=". pg_escape_string($page_num); - - $res = self::__query("SELECT timestamp FROM page_cache ". $where); - - $row = pg_fetch_assoc($res); - - if ( $row ) - self::__query("UPDATE page_cache SET data='" . pg_escape_string($data) . "' " . $where); - else - self::__query("INSERT INTO page_cache (id, page_num, data) VALUES ('" . pg_escape_string($id) . "', ". - pg_escape_string($page_num) . ", ". - "'". pg_escape_string($data) . "')"); - - } - - static function store_fonts($id, $fonts) { - self::__query("BEGIN"); - // Update the font information - self::__query("DELETE FROM page_fonts WHERE id='" . pg_escape_string($id) . "'"); - - foreach (array_keys($fonts) as $font) - self::__query("INSERT INTO page_fonts (id, font_name) VALUES ('" . - pg_escape_string($id) . "', '" . pg_escape_string($font) . "')"); - self::__query("COMMIT"); - } - -// static function retrieve_page($id, $page_num) { - -// $res = self::__query("SELECT data FROM page_cache WHERE id='" . pg_escape_string($id) . "' AND ". -// "page_num=". pg_escape_string($page_num)); - -// $row = pg_fetch_assoc($res); - -// return pg_unescape_bytea($row["data"]); - -// } - - static function get_page_timestamp($id, $page_num) { - $res = self::__query("SELECT timestamp FROM page_cache WHERE id='" . pg_escape_string($id) . "' AND ". - "page_num=". pg_escape_string($page_num)); - - $row = pg_fetch_assoc($res); - - return $row["timestamp"]; - - } - - // Adds the cached document referenced by $id to the provided pdf - static function insert_cached_document(CPDF_Adapter $pdf, $id, $new_page = true) { - $res = self::__query("SELECT font_name FROM page_fonts WHERE id='" . pg_escape_string($id) . "'"); - - // Ensure that the fonts needed by the cached document are loaded into - // the pdf - while ($row = pg_fetch_assoc($res)) - $pdf->get_cpdf()->selectFont($row["font_name"]); - - $res = self::__query("SELECT data FROM page_cache WHERE id='" . pg_escape_string($id) . "'"); - - if ( $new_page ) - $pdf->new_page(); - - $first = true; - while ($row = pg_fetch_assoc($res)) { - - if ( !$first ) - $pdf->new_page(); - else - $first = false; - - $page = $pdf->reopen_serialized_object($row["data"]); - //$pdf->close_object(); - $pdf->add_object($page, "add"); - - } - - } -} - -Page_Cache::init(); diff --git a/library/vendor/dompdf/include/page_frame_decorator.cls.php b/library/vendor/dompdf/include/page_frame_decorator.cls.php deleted file mode 100644 index f089d0075..000000000 --- a/library/vendor/dompdf/include/page_frame_decorator.cls.php +++ /dev/null @@ -1,592 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Decorates frames for page layout - * - * @access private - * @package dompdf - */ -class Page_Frame_Decorator extends Frame_Decorator { - - /** - * y value of bottom page margin - * - * @var float - */ - protected $_bottom_page_margin; - - /** - * Flag indicating page is full. - * - * @var bool - */ - protected $_page_full; - - /** - * Number of tables currently being reflowed - * - * @var int - */ - protected $_in_table; - - /** - * The pdf renderer - * - * @var Renderer - */ - protected $_renderer; - - /** - * This page's floating frames - * - * @var array - */ - protected $_floating_frames = array(); - - //........................................................................ - - /** - * Class constructor - * - * @param Frame $frame the frame to decorate - * @param DOMPDF $dompdf - */ - function __construct(Frame $frame, DOMPDF $dompdf) { - parent::__construct($frame, $dompdf); - $this->_page_full = false; - $this->_in_table = 0; - $this->_bottom_page_margin = null; - } - - /** - * Set the renderer used for this pdf - * - * @param Renderer $renderer the renderer to use - */ - function set_renderer($renderer) { - $this->_renderer = $renderer; - } - - /** - * Return the renderer used for this pdf - * - * @return Renderer - */ - function get_renderer() { - return $this->_renderer; - } - - /** - * Set the frame's containing block. Overridden to set $this->_bottom_page_margin. - * - * @param float $x - * @param float $y - * @param float $w - * @param float $h - */ - function set_containing_block($x = null, $y = null, $w = null, $h = null) { - parent::set_containing_block($x,$y,$w,$h); - //$w = $this->get_containing_block("w"); - if ( isset($h) ) - $this->_bottom_page_margin = $h; // - $this->_frame->get_style()->length_in_pt($this->_frame->get_style()->margin_bottom, $w); - } - - /** - * Returns true if the page is full and is no longer accepting frames. - * - * @return bool - */ - function is_full() { - return $this->_page_full; - } - - /** - * Start a new page by resetting the full flag. - */ - function next_page() { - $this->_floating_frames = array(); - $this->_renderer->new_page(); - $this->_page_full = false; - } - - /** - * Indicate to the page that a table is currently being reflowed. - */ - function table_reflow_start() { - $this->_in_table++; - } - - /** - * Indicate to the page that table reflow is finished. - */ - function table_reflow_end() { - $this->_in_table--; - } - - /** - * Return whether we are currently in a nested table or not - * - * @return bool - */ - function in_nested_table() { - return $this->_in_table > 1; - } - - /** - * Check if a forced page break is required before $frame. This uses the - * frame's page_break_before property as well as the preceeding frame's - * page_break_after property. - * - * @link http://www.w3.org/TR/CSS21/page.html#forced - * - * @param Frame $frame the frame to check - * @return bool true if a page break occured - */ - function check_forced_page_break(Frame $frame) { - - // Skip check if page is already split - if ( $this->_page_full ) - return null; - - $block_types = array("block", "list-item", "table", "inline"); - $page_breaks = array("always", "left", "right"); - - $style = $frame->get_style(); - - if ( !in_array($style->display, $block_types) ) - return false; - - // Find the previous block-level sibling - $prev = $frame->get_prev_sibling(); - - while ( $prev && !in_array($prev->get_style()->display, $block_types) ) - $prev = $prev->get_prev_sibling(); - - - if ( in_array($style->page_break_before, $page_breaks) ) { - - // Prevent cascading splits - $frame->split(null, true); - // We have to grab the style again here because split() resets - // $frame->style to the frame's orignal style. - $frame->get_style()->page_break_before = "auto"; - $this->_page_full = true; - - return true; - } - - if ( $prev && in_array($prev->get_style()->page_break_after, $page_breaks) ) { - // Prevent cascading splits - $frame->split(null, true); - $prev->get_style()->page_break_after = "auto"; - $this->_page_full = true; - return true; - } - - if( $prev && $prev->get_last_child() && $frame->get_node()->nodeName != "body" ) { - $prev_last_child = $prev->get_last_child(); - if ( in_array($prev_last_child->get_style()->page_break_after, $page_breaks) ) { - $frame->split(null, true); - $prev_last_child->get_style()->page_break_after = "auto"; - $this->_page_full = true; - return true; - } - } - - - return false; - } - - /** - * Determine if a page break is allowed before $frame - * http://www.w3.org/TR/CSS21/page.html#allowed-page-breaks - * - * In the normal flow, page breaks can occur at the following places: - * - * 1. In the vertical margin between block boxes. When a page - * break occurs here, the used values of the relevant - * 'margin-top' and 'margin-bottom' properties are set to '0'. - * 2. Between line boxes inside a block box. - * - * These breaks are subject to the following rules: - * - * * Rule A: Breaking at (1) is allowed only if the - * 'page-break-after' and 'page-break-before' properties of - * all the elements generating boxes that meet at this margin - * allow it, which is when at least one of them has the value - * 'always', 'left', or 'right', or when all of them are - * 'auto'. - * - * * Rule B: However, if all of them are 'auto' and the - * nearest common ancestor of all the elements has a - * 'page-break-inside' value of 'avoid', then breaking here is - * not allowed. - * - * * Rule C: Breaking at (2) is allowed only if the number of - * line boxes between the break and the start of the enclosing - * block box is the value of 'orphans' or more, and the number - * of line boxes between the break and the end of the box is - * the value of 'widows' or more. - * - * * Rule D: In addition, breaking at (2) is allowed only if - * the 'page-break-inside' property is 'auto'. - * - * If the above doesn't provide enough break points to keep - * content from overflowing the page boxes, then rules B and D are - * dropped in order to find additional breakpoints. - * - * If that still does not lead to sufficient break points, rules A - * and C are dropped as well, to find still more break points. - * - * We will also allow breaks between table rows. However, when - * splitting a table, the table headers should carry over to the - * next page (but they don't yet). - * - * @param Frame $frame the frame to check - * @return bool true if a break is allowed, false otherwise - */ - protected function _page_break_allowed(Frame $frame) { - - $block_types = array("block", "list-item", "table", "-dompdf-image"); - dompdf_debug("page-break", "_page_break_allowed(" . $frame->get_node()->nodeName. ")"); - $display = $frame->get_style()->display; - - // Block Frames (1): - if ( in_array($display, $block_types) ) { - - // Avoid breaks within table-cells - if ( $this->_in_table ) { - dompdf_debug("page-break", "In table: " . $this->_in_table); - return false; - } - - // Rules A & B - - if ( $frame->get_style()->page_break_before === "avoid" ) { - dompdf_debug("page-break", "before: avoid"); - return false; - } - - // Find the preceeding block-level sibling - $prev = $frame->get_prev_sibling(); - while ( $prev && !in_array($prev->get_style()->display, $block_types) ) - $prev = $prev->get_prev_sibling(); - - // Does the previous element allow a page break after? - if ( $prev && $prev->get_style()->page_break_after === "avoid" ) { - dompdf_debug("page-break", "after: avoid"); - return false; - } - - // If both $prev & $frame have the same parent, check the parent's - // page_break_inside property. - $parent = $frame->get_parent(); - if ( $prev && $parent && $parent->get_style()->page_break_inside === "avoid" ) { - dompdf_debug("page-break", "parent inside: avoid"); - return false; - } - - // To prevent cascading page breaks when a top-level element has - // page-break-inside: avoid, ensure that at least one frame is - // on the page before splitting. - if ( $parent->get_node()->nodeName === "body" && !$prev ) { - // We are the body's first child - dompdf_debug("page-break", "Body's first child."); - return false; - } - - // If the frame is the first block-level frame, use the value from - // $frame's parent instead. - if ( !$prev && $parent ) - return $this->_page_break_allowed( $parent ); - - dompdf_debug("page-break", "block: break allowed"); - return true; - - } - - // Inline frames (2): - else if ( in_array($display, Style::$INLINE_TYPES) ) { - - // Avoid breaks within table-cells - if ( $this->_in_table ) { - dompdf_debug("page-break", "In table: " . $this->_in_table); - return false; - } - - // Rule C - $block_parent = $frame->find_block_parent(); - if ( count($block_parent->get_line_boxes() ) < $frame->get_style()->orphans ) { - dompdf_debug("page-break", "orphans"); - return false; - } - - // FIXME: Checking widows is tricky without having laid out the - // remaining line boxes. Just ignore it for now... - - // Rule D - $p = $block_parent; - while ($p) { - if ( $p->get_style()->page_break_inside === "avoid" ) { - dompdf_debug("page-break", "parent->inside: avoid"); - return false; - } - $p = $p->find_block_parent(); - } - - // To prevent cascading page breaks when a top-level element has - // page-break-inside: avoid, ensure that at least one frame with - // some content is on the page before splitting. - $prev = $frame->get_prev_sibling(); - while ( $prev && ($prev->is_text_node() && trim($prev->get_node()->nodeValue) == "") ) - $prev = $prev->get_prev_sibling(); - - if ( $block_parent->get_node()->nodeName === "body" && !$prev ) { - // We are the body's first child - dompdf_debug("page-break", "Body's first child."); - return false; - } - - // Skip breaks on empty text nodes - if ( $frame->is_text_node() && - $frame->get_node()->nodeValue == "" ) - return false; - - dompdf_debug("page-break", "inline: break allowed"); - return true; - - // Table-rows - } else if ( $display === "table-row" ) { - - // Simply check if the parent table's page_break_inside property is - // not 'avoid' - $p = Table_Frame_Decorator::find_parent_table($frame); - - while ($p) { - if ( $p->get_style()->page_break_inside === "avoid" ) { - dompdf_debug("page-break", "parent->inside: avoid"); - return false; - } - $p = $p->find_block_parent(); - } - - // Avoid breaking after the first row of a table - if ( $p && $p->get_first_child() === $frame) { - dompdf_debug("page-break", "table: first-row"); - return false; - } - - // If this is a nested table, prevent the page from breaking - if ( $this->_in_table > 1 ) { - dompdf_debug("page-break", "table: nested table"); - return false; - } - - dompdf_debug("page-break","table-row/row-groups: break allowed"); - return true; - - } else if ( in_array($display, Table_Frame_Decorator::$ROW_GROUPS) ) { - - // Disallow breaks at row-groups: only split at row boundaries - return false; - - } else { - - dompdf_debug("page-break", "? " . $frame->get_style()->display . ""); - return false; - } - - } - - /** - * Check if $frame will fit on the page. If the frame does not fit, - * the frame tree is modified so that a page break occurs in the - * correct location. - * - * @param Frame $frame the frame to check - * @return Frame the frame following the page break - */ - function check_page_break(Frame $frame) { - // Do not split if we have already or if the frame was already - // pushed to the next page (prevents infinite loops) - if ( $this->_page_full || $frame->_already_pushed ) { - return false; - } - - // If the frame is absolute of fixed it shouldn't break - $p = $frame; - do { - if ( $p->is_absolute() ) - return false; - } while ( $p = $p->get_parent() ); - - $margin_height = $frame->get_margin_height(); - - // FIXME If the row is taller than the page and - // if it the first of the page, we don't break - if ( $frame->get_style()->display === "table-row" && - !$frame->get_prev_sibling() && - $margin_height > $this->get_margin_height() ) - return false; - - // Determine the frame's maximum y value - $max_y = $frame->get_position("y") + $margin_height; - - // If a split is to occur here, then the bottom margins & paddings of all - // parents of $frame must fit on the page as well: - $p = $frame->get_parent(); - while ( $p ) { - $style = $p->get_style(); - $max_y += $style->length_in_pt(array($style->margin_bottom, - $style->padding_bottom, - $style->border_bottom_width)); - $p = $p->get_parent(); - } - - - // Check if $frame flows off the page - if ( $max_y <= $this->_bottom_page_margin ) - // no: do nothing - return false; - - dompdf_debug("page-break", "check_page_break"); - dompdf_debug("page-break", "in_table: " . $this->_in_table); - - // yes: determine page break location - $iter = $frame; - $flg = false; - - $in_table = $this->_in_table; - - dompdf_debug("page-break","Starting search"); - while ( $iter ) { - // echo "\nbacktrack: " .$iter->get_node()->nodeName ." ".spl_object_hash($iter->get_node()). ""; - if ( $iter === $this ) { - dompdf_debug("page-break", "reached root."); - // We've reached the root in our search. Just split at $frame. - break; - } - - if ( $this->_page_break_allowed($iter) ) { - dompdf_debug("page-break","break allowed, splitting."); - $iter->split(null, true); - $this->_page_full = true; - $this->_in_table = $in_table; - $frame->_already_pushed = true; - return true; - } - - if ( !$flg && $next = $iter->get_last_child() ) { - dompdf_debug("page-break", "following last child."); - - if ( $next->is_table() ) - $this->_in_table++; - - $iter = $next; - continue; - } - - if ( $next = $iter->get_prev_sibling() ) { - dompdf_debug("page-break", "following prev sibling."); - - if ( $next->is_table() && !$iter->is_table() ) - $this->_in_table++; - - else if ( !$next->is_table() && $iter->is_table() ) - $this->_in_table--; - - $iter = $next; - $flg = false; - continue; - } - - if ( $next = $iter->get_parent() ) { - dompdf_debug("page-break", "following parent."); - - if ( $iter->is_table() ) - $this->_in_table--; - - $iter = $next; - $flg = true; - continue; - } - - break; - } - - $this->_in_table = $in_table; - - // No valid page break found. Just break at $frame. - dompdf_debug("page-break", "no valid break found, just splitting."); - - // If we are in a table, backtrack to the nearest top-level table row - if ( $this->_in_table ) { - $iter = $frame; - while ($iter && $iter->get_style()->display !== "table-row") - $iter = $iter->get_parent(); - - $iter->split(null, true); - } else { - $frame->split(null, true); - } - - $this->_page_full = true; - $frame->_already_pushed = true; - return true; - } - - //........................................................................ - - function split(Frame $frame = null, $force_pagebreak = false) { - // Do nothing - } - - /** - * Add a floating frame - * - * @param Frame $frame - * - * @return void - */ - function add_floating_frame(Frame $frame) { - array_unshift($this->_floating_frames, $frame); - } - - /** - * @return Frame[] - */ - function get_floating_frames() { - return $this->_floating_frames; - } - - public function remove_floating_frame($key) { - unset($this->_floating_frames[$key]); - } - - public function get_lowest_float_offset(Frame $child) { - $style = $child->get_style(); - $side = $style->clear; - $float = $style->float; - - $y = 0; - - foreach($this->_floating_frames as $key => $frame) { - if ( $side === "both" || $frame->get_style()->float === $side ) { - $y = max($y, $frame->get_position("y") + $frame->get_margin_height()); - - if ( $float !== "none" ) { - $this->remove_floating_frame($key); - } - } - } - - return $y; - } - -} diff --git a/library/vendor/dompdf/include/page_frame_reflower.cls.php b/library/vendor/dompdf/include/page_frame_reflower.cls.php deleted file mode 100644 index 4223f4e85..000000000 --- a/library/vendor/dompdf/include/page_frame_reflower.cls.php +++ /dev/null @@ -1,186 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Reflows pages - * - * @access private - * @package dompdf - */ -class Page_Frame_Reflower extends Frame_Reflower { - - /** - * Cache of the callbacks array - * - * @var array - */ - private $_callbacks; - - /** - * Cache of the canvas - * - * @var Canvas - */ - private $_canvas; - - function __construct(Page_Frame_Decorator $frame) { parent::__construct($frame); } - - function apply_page_style(Frame $frame, $page_number){ - $style = $frame->get_style(); - $page_styles = $style->get_stylesheet()->get_page_styles(); - - // http://www.w3.org/TR/CSS21/page.html#page-selectors - if ( count($page_styles) > 1 ) { - $odd = $page_number % 2 == 1; - $first = $page_number == 1; - - $style = clone $page_styles["base"]; - - // FIXME RTL - if ( $odd && isset($page_styles[":right"]) ) { - $style->merge($page_styles[":right"]); - } - - if ( $odd && isset($page_styles[":odd"]) ) { - $style->merge($page_styles[":odd"]); - } - - // FIXME RTL - if ( !$odd && isset($page_styles[":left"]) ) { - $style->merge($page_styles[":left"]); - } - - if ( !$odd && isset($page_styles[":even"]) ) { - $style->merge($page_styles[":even"]); - } - - if ( $first && isset($page_styles[":first"]) ) { - $style->merge($page_styles[":first"]); - } - - $frame->set_style($style); - } - } - - //........................................................................ - - /** - * Paged layout: - * http://www.w3.org/TR/CSS21/page.html - */ - function reflow(Block_Frame_Decorator $block = null) { - $fixed_children = array(); - $prev_child = null; - $child = $this->_frame->get_first_child(); - $current_page = 0; - - while ($child) { - $this->apply_page_style($this->_frame, $current_page + 1); - - $style = $this->_frame->get_style(); - - // Pages are only concerned with margins - $cb = $this->_frame->get_containing_block(); - $left = $style->length_in_pt($style->margin_left, $cb["w"]); - $right = $style->length_in_pt($style->margin_right, $cb["w"]); - $top = $style->length_in_pt($style->margin_top, $cb["h"]); - $bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]); - - $content_x = $cb["x"] + $left; - $content_y = $cb["y"] + $top; - $content_width = $cb["w"] - $left - $right; - $content_height = $cb["h"] - $top - $bottom; - - // Only if it's the first page, we save the nodes with a fixed position - if ($current_page == 0) { - $children = $child->get_children(); - foreach ($children as $onechild) { - if ($onechild->get_style()->position === "fixed") { - $fixed_children[] = $onechild->deep_copy(); - } - } - $fixed_children = array_reverse($fixed_children); - } - - $child->set_containing_block($content_x, $content_y, $content_width, $content_height); - - // Check for begin reflow callback - $this->_check_callbacks("begin_page_reflow", $child); - - //Insert a copy of each node which have a fixed position - if ($current_page >= 1) { - foreach ($fixed_children as $fixed_child) { - $child->insert_child_before($fixed_child->deep_copy(), $child->get_first_child()); - } - } - - $child->reflow(); - $next_child = $child->get_next_sibling(); - - // Check for begin render callback - $this->_check_callbacks("begin_page_render", $child); - - // Render the page - $this->_frame->get_renderer()->render($child); - - // Check for end render callback - $this->_check_callbacks("end_page_render", $child); - - if ( $next_child ) { - $this->_frame->next_page(); - } - - // Wait to dispose of all frames on the previous page - // so callback will have access to them - if ( $prev_child ) { - $prev_child->dispose(true); - } - $prev_child = $child; - $child = $next_child; - $current_page++; - } - - // Dispose of previous page if it still exists - if ( $prev_child ) { - $prev_child->dispose(true); - } - } - - //........................................................................ - - /** - * Check for callbacks that need to be performed when a given event - * gets triggered on a page - * - * @param string $event the type of event - * @param Frame $frame the frame that event is triggered on - */ - protected function _check_callbacks($event, $frame) { - if (!isset($this->_callbacks)) { - $dompdf = $this->_frame->get_dompdf(); - $this->_callbacks = $dompdf->get_callbacks(); - $this->_canvas = $dompdf->get_canvas(); - } - - if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) { - $info = array(0 => $this->_canvas, "canvas" => $this->_canvas, - 1 => $frame, "frame" => $frame); - $fs = $this->_callbacks[$event]; - foreach ($fs as $f) { - if (is_callable($f)) { - if (is_array($f)) { - $f[0]->$f[1]($info); - } else { - $f($info); - } - } - } - } - } -} diff --git a/library/vendor/dompdf/include/pdflib_adapter.cls.php b/library/vendor/dompdf/include/pdflib_adapter.cls.php deleted file mode 100644 index 2417d5f52..000000000 --- a/library/vendor/dompdf/include/pdflib_adapter.cls.php +++ /dev/null @@ -1,1085 +0,0 @@ - - * @author Helmut Tischer - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * PDF rendering interface - * - * PDFLib_Adapter provides a simple, stateless interface to the one - * provided by PDFLib. - * - * Unless otherwise mentioned, all dimensions are in points (1/72 in). - * The coordinate origin is in the top left corner and y values - * increase downwards. - * - * See {@link http://www.pdflib.com/} for more complete documentation - * on the underlying PDFlib functions. - * - * @package dompdf - */ -class PDFLib_Adapter implements Canvas { - - /** - * Dimensions of paper sizes in points - * - * @var array; - */ - static public $PAPER_SIZES = array(); // Set to CPDF_Adapter::$PAPER_SIZES below. - - /** - * Whether to create PDFs in memory or on disk - * - * @var bool - */ - static $IN_MEMORY = true; - - /** - * @var DOMPDF - */ - private $_dompdf; - - /** - * Instance of PDFLib class - * - * @var PDFlib - */ - private $_pdf; - - /** - * Name of temporary file used for PDFs created on disk - * - * @var string - */ - private $_file; - - /** - * PDF width, in points - * - * @var float - */ - private $_width; - - /** - * PDF height, in points - * - * @var float - */ - private $_height; - - /** - * Last fill color used - * - * @var array - */ - private $_last_fill_color; - - /** - * Last stroke color used - * - * @var array - */ - private $_last_stroke_color; - - /** - * Cache of image handles - * - * @var array - */ - private $_imgs; - - /** - * Cache of font handles - * - * @var array - */ - private $_fonts; - - /** - * List of objects (templates) to add to multiple pages - * - * @var array - */ - private $_objs; - - /** - * Current page number - * - * @var int - */ - private $_page_number; - - /** - * Total number of pages - * - * @var int - */ - private $_page_count; - - /** - * Text to display on every page - * - * @var array - */ - private $_page_text; - - /** - * Array of pages for accesing after rendering is initially complete - * - * @var array - */ - private $_pages; - - /** - * Class constructor - * - * @param mixed $paper The size of paper to use either a string (see {@link CPDF_Adapter::$PAPER_SIZES}) or - * an array(xmin,ymin,xmax,ymax) - * @param string $orientation The orientation of the document (either 'landscape' or 'portrait') - * @param DOMPDF $dompdf - */ - function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) { - if ( is_array($paper) ) { - $size = $paper; - } - else if ( isset(self::$PAPER_SIZES[mb_strtolower($paper)]) ) { - $size = self::$PAPER_SIZES[mb_strtolower($paper)]; - } - else { - $size = self::$PAPER_SIZES["letter"]; - } - - if ( mb_strtolower($orientation) === "landscape" ) { - list($size[2], $size[3]) = array($size[3], $size[2]); - } - - $this->_width = $size[2] - $size[0]; - $this->_height= $size[3] - $size[1]; - - $this->_dompdf = $dompdf; - - $this->_pdf = new PDFLib(); - - if ( defined("DOMPDF_PDFLIB_LICENSE") ) - $this->_pdf->set_parameter( "license", DOMPDF_PDFLIB_LICENSE); - - $this->_pdf->set_parameter("textformat", "utf8"); - $this->_pdf->set_parameter("fontwarning", "false"); - - $this->_pdf->set_info("Creator", "DOMPDF"); - - // Silence pedantic warnings about missing TZ settings - $tz = @date_default_timezone_get(); - date_default_timezone_set("UTC"); - $this->_pdf->set_info("Date", date("Y-m-d")); - date_default_timezone_set($tz); - - if ( self::$IN_MEMORY ) - $this->_pdf->begin_document("",""); - else { - $tmp_dir = $this->_dompdf->get_options("temp_dir"); - $tmp_name = tempnam($tmp_dir, "libdompdf_pdf_"); - @unlink($tmp_name); - $this->_file = "$tmp_name.pdf"; - $this->_pdf->begin_document($this->_file,""); - } - - $this->_pdf->begin_page_ext($this->_width, $this->_height, ""); - - $this->_page_number = $this->_page_count = 1; - $this->_page_text = array(); - - $this->_imgs = array(); - $this->_fonts = array(); - $this->_objs = array(); - - // Set up font paths - $families = Font_Metrics::get_font_families(); - foreach ($families as $files) { - foreach ($files as $file) { - $face = basename($file); - $afm = null; - - // Prefer ttfs to afms - if ( file_exists("$file.ttf") ) { - $outline = "$file.ttf"; - - } else if ( file_exists("$file.TTF") ) { - $outline = "$file.TTF"; - - } else if ( file_exists("$file.pfb") ) { - $outline = "$file.pfb"; - - if ( file_exists("$file.afm") ) { - $afm = "$file.afm"; - } - - } else if ( file_exists("$file.PFB") ) { - $outline = "$file.PFB"; - if ( file_exists("$file.AFM") ) { - $afm = "$file.AFM"; - } - } else { - continue; - } - - $this->_pdf->set_parameter("FontOutline", "\{$face\}=\{$outline\}"); - - if ( !is_null($afm) ) { - $this->_pdf->set_parameter("FontAFM", "\{$face\}=\{$afm\}"); - } - } - } - } - - function get_dompdf(){ - return $this->_dompdf; - } - - /** - * Close the pdf - */ - protected function _close() { - $this->_place_objects(); - - // Close all pages - $this->_pdf->suspend_page(""); - for ($p = 1; $p <= $this->_page_count; $p++) { - $this->_pdf->resume_page("pagenumber=$p"); - $this->_pdf->end_page_ext(""); - } - - $this->_pdf->end_document(""); - } - - - /** - * Returns the PDFLib instance - * - * @return PDFLib - */ - function get_pdflib() { - return $this->_pdf; - } - - /** - * Add meta information to the PDF - * - * @param string $label label of the value (Creator, Producter, etc.) - * @param string $value the text to set - */ - function add_info($label, $value) { - $this->_pdf->set_info($label, $value); - } - - /** - * Opens a new 'object' (template in PDFLib-speak) - * - * While an object is open, all drawing actions are recorded to the - * object instead of being drawn on the current page. Objects can - * be added later to a specific page or to several pages. - * - * The return value is an integer ID for the new object. - * - * @see PDFLib_Adapter::close_object() - * @see PDFLib_Adapter::add_object() - * - * @return int - */ - function open_object() { - $this->_pdf->suspend_page(""); - $ret = $this->_pdf->begin_template($this->_width, $this->_height); - $this->_pdf->save(); - $this->_objs[$ret] = array("start_page" => $this->_page_number); - return $ret; - } - - /** - * Reopen an existing object (NOT IMPLEMENTED) - * PDFLib does not seem to support reopening templates. - * - * @param int $object the ID of a previously opened object - * - * @throws DOMPDF_Exception - * @return void - */ - function reopen_object($object) { - throw new DOMPDF_Exception("PDFLib does not support reopening objects."); - } - - /** - * Close the current template - * - * @see PDFLib_Adapter::open_object() - */ - function close_object() { - $this->_pdf->restore(); - $this->_pdf->end_template(); - $this->_pdf->resume_page("pagenumber=".$this->_page_number); - } - - /** - * Adds the specified object to the document - * - * $where can be one of: - * - 'add' add to current page only - * - 'all' add to every page from the current one onwards - * - 'odd' add to all odd numbered pages from now on - * - 'even' add to all even numbered pages from now on - * - 'next' add the object to the next page only - * - 'nextodd' add to all odd numbered pages from the next one - * - 'nexteven' add to all even numbered pages from the next one - * - * @param int $object the object handle returned by open_object() - * @param string $where - */ - function add_object($object, $where = 'all') { - - if ( mb_strpos($where, "next") !== false ) { - $this->_objs[$object]["start_page"]++; - $where = str_replace("next", "", $where); - if ( $where == "" ) - $where = "add"; - } - - $this->_objs[$object]["where"] = $where; - } - - /** - * Stops the specified template from appearing in the document. - * - * The object will stop being displayed on the page following the - * current one. - * - * @param int $object - */ - function stop_object($object) { - - if ( !isset($this->_objs[$object]) ) - return; - - $start = $this->_objs[$object]["start_page"]; - $where = $this->_objs[$object]["where"]; - - // Place the object on this page if required - if ( $this->_page_number >= $start && - (($this->_page_number % 2 == 0 && $where === "even") || - ($this->_page_number % 2 == 1 && $where === "odd") || - ($where === "all")) ) { - $this->_pdf->fit_image($object, 0, 0, ""); - } - - $this->_objs[$object] = null; - unset($this->_objs[$object]); - } - - /** - * Add all active objects to the current page - */ - protected function _place_objects() { - - foreach ( $this->_objs as $obj => $props ) { - $start = $props["start_page"]; - $where = $props["where"]; - - // Place the object on this page if required - if ( $this->_page_number >= $start && - (($this->_page_number % 2 == 0 && $where === "even") || - ($this->_page_number % 2 == 1 && $where === "odd") || - ($where === "all")) ) { - $this->_pdf->fit_image($obj,0,0,""); - } - } - - } - - function get_width() { return $this->_width; } - - function get_height() { return $this->_height; } - - function get_page_number() { return $this->_page_number; } - - function get_page_count() { return $this->_page_count; } - - function set_page_number($num) { $this->_page_number = (int)$num; } - - function set_page_count($count) { $this->_page_count = (int)$count; } - - - /** - * Sets the line style - * - * @param float $width - * @param $cap - * @param string $join - * @param array $dash - * - * @return void - */ - protected function _set_line_style($width, $cap, $join, $dash) { - - if ( count($dash) == 1 ) - $dash[] = $dash[0]; - - if ( count($dash) > 1 ) - $this->_pdf->setdashpattern("dasharray={" . implode(" ", $dash) . "}"); - else - $this->_pdf->setdash(0,0); - - switch ( $join ) { - case "miter": - $this->_pdf->setlinejoin(0); - break; - - case "round": - $this->_pdf->setlinejoin(1); - break; - - case "bevel": - $this->_pdf->setlinejoin(2); - break; - - default: - break; - } - - switch ( $cap ) { - case "butt": - $this->_pdf->setlinecap(0); - break; - - case "round": - $this->_pdf->setlinecap(1); - break; - - case "square": - $this->_pdf->setlinecap(2); - break; - - default: - break; - } - - $this->_pdf->setlinewidth($width); - - } - - /** - * Sets the line color - * - * @param array $color array(r,g,b) - */ - protected function _set_stroke_color($color) { - if($this->_last_stroke_color == $color) - return; - - $this->_last_stroke_color = $color; - - if (isset($color[3])) { - $type = "cmyk"; - list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], $color[3]); - } - elseif (isset($color[2])) { - $type = "rgb"; - list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], null); - } - else { - $type = "gray"; - list($c1, $c2, $c3, $c4) = array($color[0], $color[1], null, null); - } - - $this->_pdf->setcolor("stroke", $type, $c1, $c2, $c3, $c4); - } - - /** - * Sets the fill color - * - * @param array $color array(r,g,b) - */ - protected function _set_fill_color($color) { - if($this->_last_fill_color == $color) - return; - - $this->_last_fill_color = $color; - - if (isset($color[3])) { - $type = "cmyk"; - list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], $color[3]); - } - elseif (isset($color[2])) { - $type = "rgb"; - list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], null); - } - else { - $type = "gray"; - list($c1, $c2, $c3, $c4) = array($color[0], $color[1], null, null); - } - - $this->_pdf->setcolor("fill", $type, $c1, $c2, $c3, $c4); - } - - /** - * Sets the opacity - * - * @param $opacity - * @param $mode - */ - function set_opacity($opacity, $mode = "Normal") { - if ( $mode === "Normal" ) { - $gstate = $this->_pdf->create_gstate("opacityfill=$opacity opacitystroke=$opacity"); - $this->_pdf->set_gstate($gstate); - } - } - - function set_default_view($view, $options = array()) { - // TODO - // http://www.pdflib.com/fileadmin/pdflib/pdf/manuals/PDFlib-8.0.2-API-reference.pdf - /** - * fitheight Fit the page height to the window, with the x coordinate left at the left edge of the window. - * fitrect Fit the rectangle specified by left, bottom, right, and top to the window. - * fitvisible Fit the visible contents of the page (the ArtBox) to the window. - * fitvisibleheight Fit the visible contents of the page to the window with the x coordinate left at the left edge of the window. - * fitvisiblewidth Fit the visible contents of the page to the window with the y coordinate top at the top edge of the window. - * fitwidth Fit the page width to the window, with the y coordinate top at the top edge of the window. - * fitwindow Fit the complete page to the window. - * fixed - */ - //$this->_pdf->set_parameter("openaction", $view); - } - - /** - * Loads a specific font and stores the corresponding descriptor. - * - * @param string $font - * @param string $encoding - * @param string $options - * - * @return int the font descriptor for the font - */ - protected function _load_font($font, $encoding = null, $options = "") { - - // Check if the font is a native PDF font - // Embed non-native fonts - $test = strtolower(basename($font)); - if ( in_array($test, DOMPDF::$native_fonts) ) { - $font = basename($font); - - } else { - // Embed non-native fonts - $options .= " embedding=true"; - } - - if ( is_null($encoding) ) { - - // Unicode encoding is only available for the commerical - // version of PDFlib and not PDFlib-Lite - if ( defined("DOMPDF_PDFLIB_LICENSE") ) - $encoding = "unicode"; - else - $encoding = "auto"; - - } - - $key = "$font:$encoding:$options"; - - if ( isset($this->_fonts[$key]) ) - return $this->_fonts[$key]; - - else { - - $this->_fonts[$key] = $this->_pdf->load_font($font, $encoding, $options); - return $this->_fonts[$key]; - - } - - } - - /** - * Remaps y coords from 4th to 1st quadrant - * - * @param float $y - * @return float - */ - protected function y($y) { return $this->_height - $y; } - - //........................................................................ - - /** - * @param float $x1 - * @param float $y1 - * @param float $x2 - * @param float $y2 - * @param array $color - * @param float $width - * @param array $style - */ - function line($x1, $y1, $x2, $y2, $color, $width, $style = null) { - $this->_set_line_style($width, "butt", "", $style); - $this->_set_stroke_color($color); - - $y1 = $this->y($y1); - $y2 = $this->y($y2); - - $this->_pdf->moveto($x1, $y1); - $this->_pdf->lineto($x2, $y2); - $this->_pdf->stroke(); - } - - function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = array()) { - $this->_set_line_style($width, "butt", "", $style); - $this->_set_stroke_color($color); - - $y1 = $this->y($y1); - - $this->_pdf->arc($x1, $y1, $r1, $astart, $aend); - $this->_pdf->stroke(); - } - - //........................................................................ - - function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) { - $this->_set_stroke_color($color); - $this->_set_line_style($width, "butt", "", $style); - - $y1 = $this->y($y1) - $h; - - $this->_pdf->rect($x1, $y1, $w, $h); - $this->_pdf->stroke(); - } - - //........................................................................ - - function filled_rectangle($x1, $y1, $w, $h, $color) { - $this->_set_fill_color($color); - - $y1 = $this->y($y1) - $h; - - $this->_pdf->rect(floatval($x1), floatval($y1), floatval($w), floatval($h)); - $this->_pdf->fill(); - } - - function clipping_rectangle($x1, $y1, $w, $h) { - $this->_pdf->save(); - - $y1 = $this->y($y1) - $h; - - $this->_pdf->rect(floatval($x1), floatval($y1), floatval($w), floatval($h)); - $this->_pdf->clip(); - } - - function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) { - // @todo - $this->clipping_rectangle($x1, $y1, $w, $h); - } - - function clipping_end() { - $this->_pdf->restore(); - } - - function save() { - $this->_pdf->save(); - } - - function restore() { - $this->_pdf->restore(); - } - - function rotate($angle, $x, $y) { - $pdf = $this->_pdf; - $pdf->translate($x, $this->_height-$y); - $pdf->rotate(-$angle); - $pdf->translate(-$x, -$this->_height+$y); - } - - function skew($angle_x, $angle_y, $x, $y) { - $pdf = $this->_pdf; - $pdf->translate($x, $this->_height-$y); - $pdf->skew($angle_y, $angle_x); // Needs to be inverted - $pdf->translate(-$x, -$this->_height+$y); - } - - function scale($s_x, $s_y, $x, $y) { - $pdf = $this->_pdf; - $pdf->translate($x, $this->_height-$y); - $pdf->scale($s_x, $s_y); - $pdf->translate(-$x, -$this->_height+$y); - } - - function translate($t_x, $t_y) { - $this->_pdf->translate($t_x, -$t_y); - } - - function transform($a, $b, $c, $d, $e, $f) { - $this->_pdf->concat($a, $b, $c, $d, $e, $f); - } - - //........................................................................ - - function polygon($points, $color, $width = null, $style = null, $fill = false) { - - $this->_set_fill_color($color); - $this->_set_stroke_color($color); - - if ( !$fill && isset($width) ) - $this->_set_line_style($width, "square", "miter", $style); - - $y = $this->y(array_pop($points)); - $x = array_pop($points); - $this->_pdf->moveto($x,$y); - - while (count($points) > 1) { - $y = $this->y(array_pop($points)); - $x = array_pop($points); - $this->_pdf->lineto($x,$y); - } - - if ( $fill ) - $this->_pdf->fill(); - else - $this->_pdf->closepath_stroke(); - } - - //........................................................................ - - function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false) { - - $this->_set_fill_color($color); - $this->_set_stroke_color($color); - - if ( !$fill && isset($width) ) - $this->_set_line_style($width, "round", "round", $style); - - $y = $this->y($y); - - $this->_pdf->circle($x, $y, $r); - - if ( $fill ) - $this->_pdf->fill(); - else - $this->_pdf->stroke(); - - } - - //........................................................................ - - function image($img_url, $x, $y, $w, $h, $resolution = "normal") { - $w = (int)$w; - $h = (int)$h; - - $img_type = Image_Cache::detect_type($img_url, $this->_dompdf->get_http_context()); - $img_ext = Image_Cache::type_to_ext($img_type); - - if ( !isset($this->_imgs[$img_url]) ) { - $this->_imgs[$img_url] = $this->_pdf->load_image($img_ext, $img_url, ""); - } - - $img = $this->_imgs[$img_url]; - - $y = $this->y($y) - $h; - $this->_pdf->fit_image($img, $x, $y, 'boxsize={'."$w $h".'} fitmethod=entire'); - } - - //........................................................................ - - function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_spacing = 0, $char_spacing = 0, $angle = 0) { - $fh = $this->_load_font($font); - - $this->_pdf->setfont($fh, $size); - $this->_set_fill_color($color); - - $y = $this->y($y) - Font_Metrics::get_font_height($font, $size); - - $word_spacing = (float)$word_spacing; - $char_spacing = (float)$char_spacing; - $angle = -(float)$angle; - - $this->_pdf->fit_textline($text, $x, $y, "rotate=$angle wordspacing=$word_spacing charspacing=$char_spacing "); - - } - - //........................................................................ - - function javascript($code) { - if ( defined("DOMPDF_PDFLIB_LICENSE") ) { - $this->_pdf->create_action("JavaScript", $code); - } - } - - //........................................................................ - - /** - * Add a named destination (similar to ... in html) - * - * @param string $anchorname The name of the named destination - */ - function add_named_dest($anchorname) { - $this->_pdf->add_nameddest($anchorname,""); - } - - //........................................................................ - - /** - * Add a link to the pdf - * - * @param string $url The url to link to - * @param float $x The x position of the link - * @param float $y The y position of the link - * @param float $width The width of the link - * @param float $height The height of the link - */ - function add_link($url, $x, $y, $width, $height) { - - $y = $this->y($y) - $height; - if ( strpos($url, '#') === 0 ) { - // Local link - $name = substr($url,1); - if ( $name ) - $this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={$url} destname=". substr($url,1) . " linewidth=0"); - } else { - - list($proto, $host, $path, $file) = explode_url($url); - - if ( $proto == "" || $proto === "file://" ) - return; // Local links are not allowed - $url = build_url($proto, $host, $path, $file); - $url = '{' . rawurldecode($url) . '}'; - - $action = $this->_pdf->create_action("URI", "url=" . $url); - $this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={$url} action={activate=$action} linewidth=0"); - } - } - - //........................................................................ - - function get_text_width($text, $font, $size, $word_spacing = 0, $letter_spacing = 0) { - $fh = $this->_load_font($font); - - // Determine the additional width due to extra spacing - $num_spaces = mb_substr_count($text," "); - $delta = $word_spacing * $num_spaces; - - if ( $letter_spacing ) { - $num_chars = mb_strlen($text); - $delta += ($num_chars - $num_spaces) * $letter_spacing; - } - - return $this->_pdf->stringwidth($text, $fh, $size) + $delta; - } - - //........................................................................ - - function get_font_height($font, $size) { - - $fh = $this->_load_font($font); - - $this->_pdf->setfont($fh, $size); - - $asc = $this->_pdf->get_value("ascender", $fh); - $desc = $this->_pdf->get_value("descender", $fh); - - // $desc is usually < 0, - $ratio = $this->_dompdf->get_option("font_height_ratio"); - return $size * ($asc - $desc) * $ratio; - } - - function get_font_baseline($font, $size) { - $ratio = $this->_dompdf->get_option("font_height_ratio"); - return $this->get_font_height($font, $size) / $ratio * 1.1; - } - - //........................................................................ - - /** - * Writes text at the specified x and y coordinates on every page - * - * The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced - * with their current values. - * - * See {@link Style::munge_color()} for the format of the color array. - * - * @param float $x - * @param float $y - * @param string $text the text to write - * @param string $font the font file to use - * @param float $size the font size, in points - * @param array $color - * @param float $word_space word spacing adjustment - * @param float $char_space char spacing adjustment - * @param float $angle angle to write the text at, measured CW starting from the x-axis - */ - function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) { - $_t = "text"; - $this->_page_text[] = compact("_t", "x", "y", "text", "font", "size", "color", "word_space", "char_space", "angle"); - } - - //........................................................................ - - /** - * Processes a script on every page - * - * The variables $pdf, $PAGE_NUM, and $PAGE_COUNT are available. - * - * This function can be used to add page numbers to all pages - * after the first one, for example. - * - * @param string $code the script code - * @param string $type the language type for script - */ - function page_script($code, $type = "text/php") { - $_t = "script"; - $this->_page_text[] = compact("_t", "code", "type"); - } - - //........................................................................ - - function new_page() { - - // Add objects to the current page - $this->_place_objects(); - - $this->_pdf->suspend_page(""); - $this->_pdf->begin_page_ext($this->_width, $this->_height, ""); - $this->_page_number = ++$this->_page_count; - - } - - //........................................................................ - - /** - * Add text to each page after rendering is complete - */ - protected function _add_page_text() { - - if ( !count($this->_page_text) ) - return; - - $this->_pdf->suspend_page(""); - - for ($p = 1; $p <= $this->_page_count; $p++) { - $this->_pdf->resume_page("pagenumber=$p"); - - foreach ($this->_page_text as $pt) { - extract($pt); - - switch ($_t) { - - case "text": - $text = str_replace(array("{PAGE_NUM}","{PAGE_COUNT}"), - array($p, $this->_page_count), $text); - $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle); - break; - - case "script": - if (!$eval) { - $eval = new PHP_Evaluator($this); - } - $eval->evaluate($code, array('PAGE_NUM' => $p, 'PAGE_COUNT' => $this->_page_count)); - break; - } - } - - $this->_pdf->suspend_page(""); - } - - $this->_pdf->resume_page("pagenumber=".$this->_page_number); - } - - //........................................................................ - - function stream($filename, $options = null) { - - // Add page text - $this->_add_page_text(); - - if ( isset($options["compress"]) && $options["compress"] != 1 ) - $this->_pdf->set_value("compress", 0); - else - $this->_pdf->set_value("compress", 6); - - $this->_close(); - - $data = ""; - - if ( self::$IN_MEMORY ) { - $data = $this->_pdf->get_buffer(); - //$size = strlen($data); - } else { - //$size = filesize($this->_file); - } - - - $filename = str_replace(array("\n","'"),"", $filename); - $attach = (isset($options["Attachment"]) && $options["Attachment"]) ? "attachment" : "inline"; - - header("Cache-Control: private"); - header("Content-type: application/pdf"); - header("Content-Disposition: $attach; filename=\"$filename\""); - - //header("Content-length: " . $size); - - if ( self::$IN_MEMORY ) - echo $data; - - else { - - // Chunked readfile() - $chunk = (1 << 21); // 2 MB - $fh = fopen($this->_file, "rb"); - if ( !$fh ) - throw new DOMPDF_Exception("Unable to load temporary PDF file: " . $this->_file); - - while ( !feof($fh) ) - echo fread($fh,$chunk); - fclose($fh); - - //debugpng - if (DEBUGPNG) print '[pdflib stream unlink '.$this->_file.']'; - if (!DEBUGKEEPTEMP) - - unlink($this->_file); - $this->_file = null; - unset($this->_file); - } - - flush(); - } - - //........................................................................ - - function output($options = null) { - - // Add page text - $this->_add_page_text(); - - if ( isset($options["compress"]) && $options["compress"] != 1 ) - $this->_pdf->set_value("compress", 0); - else - $this->_pdf->set_value("compress", 6); - - $this->_close(); - - if ( self::$IN_MEMORY ) - $data = $this->_pdf->get_buffer(); - - else { - $data = file_get_contents($this->_file); - - //debugpng - if (DEBUGPNG) print '[pdflib output unlink '.$this->_file.']'; - if (!DEBUGKEEPTEMP) - - unlink($this->_file); - $this->_file = null; - unset($this->_file); - } - - return $data; - } -} - -// Workaround for idiotic limitation on statics... -PDFLib_Adapter::$PAPER_SIZES = CPDF_Adapter::$PAPER_SIZES; diff --git a/library/vendor/dompdf/include/php_evaluator.cls.php b/library/vendor/dompdf/include/php_evaluator.cls.php deleted file mode 100644 index 38896675d..000000000 --- a/library/vendor/dompdf/include/php_evaluator.cls.php +++ /dev/null @@ -1,48 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Executes inline PHP code during the rendering process - * - * @access private - * @package dompdf - */ -class PHP_Evaluator { - - /** - * @var Canvas - */ - protected $_canvas; - - function __construct(Canvas $canvas) { - $this->_canvas = $canvas; - } - - function evaluate($code, $vars = array()) { - if ( !$this->_canvas->get_dompdf()->get_option("enable_php") ) { - return; - } - - // Set up some variables for the inline code - $pdf = $this->_canvas; - $PAGE_NUM = $pdf->get_page_number(); - $PAGE_COUNT = $pdf->get_page_count(); - - // Override those variables if passed in - foreach ($vars as $k => $v) { - $$k = $v; - } - - //$code = html_entity_decode($code); // @todo uncomment this when tested - eval($code); - } - - function render(Frame $frame) { - $this->evaluate($frame->get_node()->nodeValue); - } -} diff --git a/library/vendor/dompdf/include/positioner.cls.php b/library/vendor/dompdf/include/positioner.cls.php deleted file mode 100644 index 6a8b9edc1..000000000 --- a/library/vendor/dompdf/include/positioner.cls.php +++ /dev/null @@ -1,51 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Base Positioner class - * - * Defines postioner interface - * - * @access private - * @package dompdf - */ -abstract class Positioner { - - /** - * @var Frame_Decorator - */ - protected $_frame; - - //........................................................................ - - function __construct(Frame_Decorator $frame) { - $this->_frame = $frame; - } - - /** - * Class destructor - */ - function __destruct() { - clear_object($this); - } - //........................................................................ - - abstract function position(); - - function move($offset_x, $offset_y, $ignore_self = false) { - list($x, $y) = $this->_frame->get_position(); - - if ( !$ignore_self ) { - $this->_frame->set_position($x + $offset_x, $y + $offset_y); - } - - foreach($this->_frame->get_children() as $child) { - $child->move($offset_x, $offset_y); - } - } -} diff --git a/library/vendor/dompdf/include/renderer.cls.php b/library/vendor/dompdf/include/renderer.cls.php deleted file mode 100644 index ceff4775c..000000000 --- a/library/vendor/dompdf/include/renderer.cls.php +++ /dev/null @@ -1,290 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Concrete renderer - * - * Instantiates several specific renderers in order to render any given - * frame. - * - * @access private - * @package dompdf - */ -class Renderer extends Abstract_Renderer { - - /** - * Array of renderers for specific frame types - * - * @var Abstract_Renderer[] - */ - protected $_renderers; - - /** - * Cache of the callbacks array - * - * @var array - */ - private $_callbacks; - - /** - * Class destructor - */ - function __destruct() { - clear_object($this); - } - - /** - * Advance the canvas to the next page - */ - function new_page() { - $this->_canvas->new_page(); - } - - /** - * Render frames recursively - * - * @param Frame $frame the frame to render - */ - function render(Frame $frame) { - global $_dompdf_debug; - - if ( $_dompdf_debug ) { - echo $frame; - flush(); - } - - $style = $frame->get_style(); - - if ( in_array($style->visibility, array("hidden", "collapse")) ) { - return; - } - - $display = $style->display; - - // Starts the CSS transformation - if ( $style->transform && is_array($style->transform) ) { - $this->_canvas->save(); - list($x, $y) = $frame->get_padding_box(); - $origin = $style->transform_origin; - - foreach($style->transform as $transform) { - list($function, $values) = $transform; - if ( $function === "matrix" ) { - $function = "transform"; - } - - $values = array_map("floatval", $values); - $values[] = $x + $style->length_in_pt($origin[0], $style->width); - $values[] = $y + $style->length_in_pt($origin[1], $style->height); - - call_user_func_array(array($this->_canvas, $function), $values); - } - } - - switch ($display) { - - case "block": - case "list-item": - case "inline-block": - case "table": - case "inline-table": - $this->_render_frame("block", $frame); - break; - - case "inline": - if ( $frame->is_text_node() ) - $this->_render_frame("text", $frame); - else - $this->_render_frame("inline", $frame); - break; - - case "table-cell": - $this->_render_frame("table-cell", $frame); - break; - - case "table-row-group": - case "table-header-group": - case "table-footer-group": - $this->_render_frame("table-row-group", $frame); - break; - - case "-dompdf-list-bullet": - $this->_render_frame("list-bullet", $frame); - break; - - case "-dompdf-image": - $this->_render_frame("image", $frame); - break; - - case "none": - $node = $frame->get_node(); - - if ( $node->nodeName === "script" ) { - if ( $node->getAttribute("type") === "text/php" || - $node->getAttribute("language") === "php" ) { - // Evaluate embedded php scripts - $this->_render_frame("php", $frame); - } - - elseif ( $node->getAttribute("type") === "text/javascript" || - $node->getAttribute("language") === "javascript" ) { - // Insert JavaScript - $this->_render_frame("javascript", $frame); - } - } - - // Don't render children, so skip to next iter - return; - - default: - break; - - } - - // Starts the overflow: hidden box - if ( $style->overflow === "hidden" ) { - list($x, $y, $w, $h) = $frame->get_padding_box(); - - // get border radii - $style = $frame->get_style(); - list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h); - - if ( $tl + $tr + $br + $bl > 0 ) { - $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl); - } - else { - $this->_canvas->clipping_rectangle($x, $y, $w, $h); - } - } - - $stack = array(); - - foreach ($frame->get_children() as $child) { - // < 0 : nagative z-index - // = 0 : no z-index, no stacking context - // = 1 : stacking context without z-index - // > 1 : z-index - $child_style = $child->get_style(); - $child_z_index = $child_style->z_index; - $z_index = 0; - - if ( $child_z_index !== "auto" ) { - $z_index = intval($child_z_index) + 1; - } - elseif ( $child_style->float !== "none" || $child->is_positionned()) { - $z_index = 1; - } - - $stack[$z_index][] = $child; - } - - ksort($stack); - - foreach ($stack as $by_index) { - foreach($by_index as $child) { - $this->render($child); - } - } - - // Ends the overflow: hidden box - if ( $style->overflow === "hidden" ) { - $this->_canvas->clipping_end(); - } - - if ( $style->transform && is_array($style->transform) ) { - $this->_canvas->restore(); - } - - // Check for end frame callback - $this->_check_callbacks("end_frame", $frame); - } - - /** - * Check for callbacks that need to be performed when a given event - * gets triggered on a frame - * - * @param string $event the type of event - * @param Frame $frame the frame that event is triggered on - */ - protected function _check_callbacks($event, $frame) { - if (!isset($this->_callbacks)) { - $this->_callbacks = $this->_dompdf->get_callbacks(); - } - - if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) { - $info = array(0 => $this->_canvas, "canvas" => $this->_canvas, - 1 => $frame, "frame" => $frame); - $fs = $this->_callbacks[$event]; - foreach ($fs as $f) { - if (is_callable($f)) { - if (is_array($f)) { - $f[0]->$f[1]($info); - } else { - $f($info); - } - } - } - } - } - - /** - * Render a single frame - * - * Creates Renderer objects on demand - * - * @param string $type type of renderer to use - * @param Frame $frame the frame to render - */ - protected function _render_frame($type, $frame) { - - if ( !isset($this->_renderers[$type]) ) { - - switch ($type) { - case "block": - $this->_renderers[$type] = new Block_Renderer($this->_dompdf); - break; - - case "inline": - $this->_renderers[$type] = new Inline_Renderer($this->_dompdf); - break; - - case "text": - $this->_renderers[$type] = new Text_Renderer($this->_dompdf); - break; - - case "image": - $this->_renderers[$type] = new Image_Renderer($this->_dompdf); - break; - - case "table-cell": - $this->_renderers[$type] = new Table_Cell_Renderer($this->_dompdf); - break; - - case "table-row-group": - $this->_renderers[$type] = new Table_Row_Group_Renderer($this->_dompdf); - break; - - case "list-bullet": - $this->_renderers[$type] = new List_Bullet_Renderer($this->_dompdf); - break; - - case "php": - $this->_renderers[$type] = new PHP_Evaluator($this->_canvas); - break; - - case "javascript": - $this->_renderers[$type] = new Javascript_Embedder($this->_dompdf); - break; - - } - } - - $this->_renderers[$type]->render($frame); - - } -} diff --git a/library/vendor/dompdf/include/style.cls.php b/library/vendor/dompdf/include/style.cls.php deleted file mode 100644 index d08fb4baa..000000000 --- a/library/vendor/dompdf/include/style.cls.php +++ /dev/null @@ -1,2435 +0,0 @@ - - * @author Helmut Tischer - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Represents CSS properties. - * - * The Style class is responsible for handling and storing CSS properties. - * It includes methods to resolve colors and lengths, as well as getters & - * setters for many CSS properites. - * - * Actual CSS parsing is performed in the {@link Stylesheet} class. - * - * @package dompdf - */ -class Style { - - const CSS_IDENTIFIER = "-?[_a-zA-Z]+[_a-zA-Z0-9-]*"; - const CSS_INTEGER = "-?\d+"; - - /** - * Default font size, in points. - * - * @var float - */ - static $default_font_size = 12; - - /** - * Default line height, as a fraction of the font size. - * - * @var float - */ - static $default_line_height = 1.2; - - /** - * Default "absolute" font sizes relative to the default font-size - * http://www.w3.org/TR/css3-fonts/#font-size-the-font-size-property - * @var array - */ - static $font_size_keywords = array( - "xx-small" => 0.6, // 3/5 - "x-small" => 0.75, // 3/4 - "small" => 0.889, // 8/9 - "medium" => 1, // 1 - "large" => 1.2, // 6/5 - "x-large" => 1.5, // 3/2 - "xx-large" => 2.0, // 2/1 - ); - - /** - * List of all inline types. Should really be a constant. - * - * @var array - */ - static $INLINE_TYPES = array("inline"); - - /** - * List of all block types. Should really be a constant. - * - * @var array - */ - static $BLOCK_TYPES = array("block", "inline-block", "table-cell", "list-item"); - - /** - * List of all positionned types. Should really be a constant. - * - * @var array - */ - static $POSITIONNED_TYPES = array("relative", "absolute", "fixed"); - - /** - * List of all table types. Should really be a constant. - * - * @var array; - */ - static $TABLE_TYPES = array("table", "inline-table"); - - /** - * List of valid border styles. Should also really be a constant. - * - * @var array - */ - static $BORDER_STYLES = array("none", "hidden", "dotted", "dashed", "solid", - "double", "groove", "ridge", "inset", "outset"); - - /** - * Default style values. - * - * @link http://www.w3.org/TR/CSS21/propidx.html - * - * @var array - */ - static protected $_defaults = null; - - /** - * List of inherited properties - * - * @link http://www.w3.org/TR/CSS21/propidx.html - * - * @var array - */ - static protected $_inherited = null; - - /** - * Caches method_exists result - * - * @var array - */ - static protected $_methods_cache = array(); - - /** - * The stylesheet this style belongs to - * - * @see Stylesheet - * @var Stylesheet - */ - protected $_stylesheet; // stylesheet this style is attached to - - /** - * Main array of all CSS properties & values - * - * @var array - */ - protected $_props; - - /* var instead of protected would allow access outside of class */ - protected $_important_props; - - /** - * Cached property values - * - * @var array - */ - protected $_prop_cache; - - /** - * Font size of parent element in document tree. Used for relative font - * size resolution. - * - * @var float - */ - protected $_parent_font_size; // Font size of parent element - - protected $_font_family; - - /** - * @var Frame - */ - protected $_frame; - - /** - * The origin of the style - * - * @var int - */ - protected $_origin = Stylesheet::ORIG_AUTHOR; - - // private members - /** - * True once the font size is resolved absolutely - * - * @var bool - */ - private $__font_size_calculated; // Cache flag - - /** - * The computed border radius - */ - private $_computed_border_radius = null; - - /** - * @var bool - */ - public $_has_border_radius = false; - - /** - * Class constructor - * - * @param Stylesheet $stylesheet the stylesheet this Style is associated with. - * @param int $origin - */ - function __construct(Stylesheet $stylesheet, $origin = Stylesheet::ORIG_AUTHOR) { - $this->_props = array(); - $this->_important_props = array(); - $this->_stylesheet = $stylesheet; - $this->_origin = $origin; - $this->_parent_font_size = null; - $this->__font_size_calculated = false; - - if ( !isset(self::$_defaults) ) { - - // Shorthand - $d =& self::$_defaults; - - // All CSS 2.1 properties, and their default values - $d["azimuth"] = "center"; - $d["background_attachment"] = "scroll"; - $d["background_color"] = "transparent"; - $d["background_image"] = "none"; - $d["background_image_resolution"] = "normal"; - $d["_dompdf_background_image_resolution"] = $d["background_image_resolution"]; - $d["background_position"] = "0% 0%"; - $d["background_repeat"] = "repeat"; - $d["background"] = ""; - $d["border_collapse"] = "separate"; - $d["border_color"] = ""; - $d["border_spacing"] = "0"; - $d["border_style"] = ""; - $d["border_top"] = ""; - $d["border_right"] = ""; - $d["border_bottom"] = ""; - $d["border_left"] = ""; - $d["border_top_color"] = ""; - $d["border_right_color"] = ""; - $d["border_bottom_color"] = ""; - $d["border_left_color"] = ""; - $d["border_top_style"] = "none"; - $d["border_right_style"] = "none"; - $d["border_bottom_style"] = "none"; - $d["border_left_style"] = "none"; - $d["border_top_width"] = "medium"; - $d["border_right_width"] = "medium"; - $d["border_bottom_width"] = "medium"; - $d["border_left_width"] = "medium"; - $d["border_width"] = "medium"; - $d["border_bottom_left_radius"] = ""; - $d["border_bottom_right_radius"] = ""; - $d["border_top_left_radius"] = ""; - $d["border_top_right_radius"] = ""; - $d["border_radius"] = ""; - $d["border"] = ""; - $d["bottom"] = "auto"; - $d["caption_side"] = "top"; - $d["clear"] = "none"; - $d["clip"] = "auto"; - $d["color"] = "#000000"; - $d["content"] = "normal"; - $d["counter_increment"] = "none"; - $d["counter_reset"] = "none"; - $d["cue_after"] = "none"; - $d["cue_before"] = "none"; - $d["cue"] = ""; - $d["cursor"] = "auto"; - $d["direction"] = "ltr"; - $d["display"] = "inline"; - $d["elevation"] = "level"; - $d["empty_cells"] = "show"; - $d["float"] = "none"; - $d["font_family"] = $stylesheet->get_dompdf()->get_option("default_font"); - $d["font_size"] = "medium"; - $d["font_style"] = "normal"; - $d["font_variant"] = "normal"; - $d["font_weight"] = "normal"; - $d["font"] = ""; - $d["height"] = "auto"; - $d["image_resolution"] = "normal"; - $d["_dompdf_image_resolution"] = $d["image_resolution"]; - $d["_dompdf_keep"] = ""; - $d["left"] = "auto"; - $d["letter_spacing"] = "normal"; - $d["line_height"] = "normal"; - $d["list_style_image"] = "none"; - $d["list_style_position"] = "outside"; - $d["list_style_type"] = "disc"; - $d["list_style"] = ""; - $d["margin_right"] = "0"; - $d["margin_left"] = "0"; - $d["margin_top"] = "0"; - $d["margin_bottom"] = "0"; - $d["margin"] = ""; - $d["max_height"] = "none"; - $d["max_width"] = "none"; - $d["min_height"] = "0"; - $d["min_width"] = "0"; - $d["opacity"] = "1.0"; // CSS3 - $d["orphans"] = "2"; - $d["outline_color"] = ""; // "invert" special color is not supported - $d["outline_style"] = "none"; - $d["outline_width"] = "medium"; - $d["outline"] = ""; - $d["overflow"] = "visible"; - $d["padding_top"] = "0"; - $d["padding_right"] = "0"; - $d["padding_bottom"] = "0"; - $d["padding_left"] = "0"; - $d["padding"] = ""; - $d["page_break_after"] = "auto"; - $d["page_break_before"] = "auto"; - $d["page_break_inside"] = "auto"; - $d["pause_after"] = "0"; - $d["pause_before"] = "0"; - $d["pause"] = ""; - $d["pitch_range"] = "50"; - $d["pitch"] = "medium"; - $d["play_during"] = "auto"; - $d["position"] = "static"; - $d["quotes"] = ""; - $d["richness"] = "50"; - $d["right"] = "auto"; - $d["size"] = "auto"; // @page - $d["speak_header"] = "once"; - $d["speak_numeral"] = "continuous"; - $d["speak_punctuation"] = "none"; - $d["speak"] = "normal"; - $d["speech_rate"] = "medium"; - $d["stress"] = "50"; - $d["table_layout"] = "auto"; - $d["text_align"] = "left"; - $d["text_decoration"] = "none"; - $d["text_indent"] = "0"; - $d["text_transform"] = "none"; - $d["top"] = "auto"; - $d["transform"] = "none"; // CSS3 - $d["transform_origin"] = "50% 50%"; // CSS3 - $d["_webkit_transform"] = $d["transform"]; // CSS3 - $d["_webkit_transform_origin"] = $d["transform_origin"]; // CSS3 - $d["unicode_bidi"] = "normal"; - $d["vertical_align"] = "baseline"; - $d["visibility"] = "visible"; - $d["voice_family"] = ""; - $d["volume"] = "medium"; - $d["white_space"] = "normal"; - $d["word_wrap"] = "normal"; - $d["widows"] = "2"; - $d["width"] = "auto"; - $d["word_spacing"] = "normal"; - $d["z_index"] = "auto"; - - // for @font-face - $d["src"] = ""; - $d["unicode_range"] = ""; - - // Properties that inherit by default - self::$_inherited = array( - "azimuth", - "background_image_resolution", - "border_collapse", - "border_spacing", - "caption_side", - "color", - "cursor", - "direction", - "elevation", - "empty_cells", - "font_family", - "font_size", - "font_style", - "font_variant", - "font_weight", - "font", - "image_resolution", - "letter_spacing", - "line_height", - "list_style_image", - "list_style_position", - "list_style_type", - "list_style", - "orphans", - "page_break_inside", - "pitch_range", - "pitch", - "quotes", - "richness", - "speak_header", - "speak_numeral", - "speak_punctuation", - "speak", - "speech_rate", - "stress", - "text_align", - "text_indent", - "text_transform", - "visibility", - "voice_family", - "volume", - "white_space", - "word_wrap", - "widows", - "word_spacing", - ); - } - } - - /** - * "Destructor": forcibly free all references held by this object - */ - function dispose() { - clear_object($this); - } - - function set_frame(Frame $frame) { - $this->_frame = $frame; - } - - function get_frame() { - return $this->_frame; - } - - function set_origin($origin) { - $this->_origin = $origin; - } - - function get_origin() { - return $this->_origin; - } - - /** - * returns the {@link Stylesheet} this Style is associated with. - * - * @return Stylesheet - */ - function get_stylesheet() { return $this->_stylesheet; } - - /** - * Converts any CSS length value into an absolute length in points. - * - * length_in_pt() takes a single length (e.g. '1em') or an array of - * lengths and returns an absolute length. If an array is passed, then - * the return value is the sum of all elements. - * - * If a reference size is not provided, the default font size is used - * ({@link Style::$default_font_size}). - * - * @param float|array $length the length or array of lengths to resolve - * @param float $ref_size an absolute reference size to resolve percentage lengths - * @return float - */ - function length_in_pt($length, $ref_size = null) { - static $cache = array(); - - if ( !is_array($length) ) { - $length = array($length); - } - - if ( !isset($ref_size) ) { - $ref_size = self::$default_font_size; - } - - $key = implode("@", $length)."/$ref_size"; - - if ( isset($cache[$key]) ) { - return $cache[$key]; - } - - $ret = 0; - foreach ($length as $l) { - - if ( $l === "auto" ) { - return "auto"; - } - - if ( $l === "none" ) { - return "none"; - } - - // Assume numeric values are already in points - if ( is_numeric($l) ) { - $ret += $l; - continue; - } - - if ( $l === "normal" ) { - $ret += $ref_size; - continue; - } - - // Border lengths - if ( $l === "thin" ) { - $ret += 0.5; - continue; - } - - if ( $l === "medium" ) { - $ret += 1.5; - continue; - } - - if ( $l === "thick" ) { - $ret += 2.5; - continue; - } - - if ( ($i = mb_strpos($l, "px")) !== false ) { - $dpi = $this->_stylesheet->get_dompdf()->get_option("dpi"); - $ret += ( mb_substr($l, 0, $i) * 72 ) / $dpi; - continue; - } - - if ( ($i = mb_strpos($l, "pt")) !== false ) { - $ret += (float)mb_substr($l, 0, $i); - continue; - } - - if ( ($i = mb_strpos($l, "%")) !== false ) { - $ret += (float)mb_substr($l, 0, $i)/100 * $ref_size; - continue; - } - - if ( ($i = mb_strpos($l, "rem")) !== false ) { - $ret += (float)mb_substr($l, 0, $i) * $this->_stylesheet->get_dompdf()->get_tree()->get_root()->get_style()->font_size; - continue; - } - - if ( ($i = mb_strpos($l, "em")) !== false ) { - $ret += (float)mb_substr($l, 0, $i) * $this->__get("font_size"); - continue; - } - - if ( ($i = mb_strpos($l, "cm")) !== false ) { - $ret += mb_substr($l, 0, $i) * 72 / 2.54; - continue; - } - - if ( ($i = mb_strpos($l, "mm")) !== false ) { - $ret += mb_substr($l, 0, $i) * 72 / 25.4; - continue; - } - - // FIXME: em:ex ratio? - if ( ($i = mb_strpos($l, "ex")) !== false ) { - $ret += mb_substr($l, 0, $i) * $this->__get("font_size") / 2; - continue; - } - - if ( ($i = mb_strpos($l, "in")) !== false ) { - $ret += (float)mb_substr($l, 0, $i) * 72; - continue; - } - - if ( ($i = mb_strpos($l, "pc")) !== false ) { - $ret += (float)mb_substr($l, 0, $i) * 12; - continue; - } - - // Bogus value - $ret += $ref_size; - } - - return $cache[$key] = $ret; - } - - - /** - * Set inherited properties in this style using values in $parent - * - * @param Style $parent - * - * @return Style - */ - function inherit(Style $parent) { - - // Set parent font size - $this->_parent_font_size = $parent->get_font_size(); - - foreach (self::$_inherited as $prop) { - //inherit the !important property also. - //if local property is also !important, don't inherit. - if ( isset($parent->_props[$prop]) && - ( !isset($this->_props[$prop]) || - ( isset($parent->_important_props[$prop]) && !isset($this->_important_props[$prop]) ) - ) - ) { - if ( isset($parent->_important_props[$prop]) ) { - $this->_important_props[$prop] = true; - } - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$prop] = null; - $this->_props[$prop] = $parent->_props[$prop]; - } - } - - foreach ($this->_props as $prop => $value) { - if ( $value === "inherit" ) { - if ( isset($parent->_important_props[$prop]) ) { - $this->_important_props[$prop] = true; - } - //do not assign direct, but - //implicite assignment through __set, redirect to specialized, get value with __get - //This is for computing defaults if the parent setting is also missing. - //Therefore do not directly assign the value without __set - //set _important_props before that to be able to propagate. - //see __set and __get, on all assignments clear cache! - //$this->_prop_cache[$prop] = null; - //$this->_props[$prop] = $parent->_props[$prop]; - //props_set for more obvious explicite assignment not implemented, because - //too many implicite uses. - // $this->props_set($prop, $parent->$prop); - $this->__set($prop, $parent->__get($prop)); - } - } - - return $this; - } - - /** - * Override properties in this style with those in $style - * - * @param Style $style - */ - function merge(Style $style) { - //treat the !important attribute - //if old rule has !important attribute, override with new rule only if - //the new rule is also !important - foreach($style->_props as $prop => $val ) { - if (isset($style->_important_props[$prop])) { - $this->_important_props[$prop] = true; - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$prop] = null; - $this->_props[$prop] = $val; - } - else if ( !isset($this->_important_props[$prop]) ) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$prop] = null; - $this->_props[$prop] = $val; - } - } - - if ( isset($style->_props["font_size"]) ) { - $this->__font_size_calculated = false; - } - } - - /** - * Returns an array(r, g, b, "r"=> r, "g"=>g, "b"=>b, "hex"=>"#rrggbb") - * based on the provided CSS color value. - * - * @param string $color - * @return array - */ - function munge_color($color) { - return CSS_Color::parse($color); - } - - /* direct access to _important_props array from outside would work only when declared as - * 'var $_important_props;' instead of 'protected $_important_props;' - * Don't call _set/__get on missing attribute. Therefore need a special access. - * Assume that __set will be also called when this is called, so do not check validity again. - * Only created, if !important exists -> always set true. - */ - function important_set($prop) { - $prop = str_replace("-", "_", $prop); - $this->_important_props[$prop] = true; - } - - function important_get($prop) { - return isset($this->_important_props[$prop]); - } - - /** - * PHP5 overloaded setter - * - * This function along with {@link Style::__get()} permit a user of the - * Style class to access any (CSS) property using the following syntax: - * - * Style->margin_top = "1em"; - * echo (Style->margin_top); - * - * - * __set() automatically calls the provided set function, if one exists, - * otherwise it sets the property directly. Typically, __set() is not - * called directly from outside of this class. - * - * On each modification clear cache to return accurate setting. - * Also affects direct settings not using __set - * For easier finding all assignments, attempted to allowing only explicite assignment: - * Very many uses, e.g. frame_reflower.cls.php -> for now leave as it is - * function __set($prop, $val) { - * throw new DOMPDF_Exception("Implicite replacement of assignment by __set. Not good."); - * } - * function props_set($prop, $val) { ... } - * - * @param string $prop the property to set - * @param mixed $val the value of the property - * - */ - function __set($prop, $val) { - $prop = str_replace("-", "_", $prop); - $this->_prop_cache[$prop] = null; - - if ( !isset(self::$_defaults[$prop]) ) { - global $_dompdf_warnings; - $_dompdf_warnings[] = "'$prop' is not a valid CSS2 property."; - return; - } - - if ( $prop !== "content" && is_string($val) && strlen($val) > 5 && mb_strpos($val, "url") === false ) { - $val = mb_strtolower(trim(str_replace(array("\n", "\t"), array(" "), $val))); - $val = preg_replace("/([0-9]+) (pt|px|pc|em|ex|in|cm|mm|%)/S", "\\1\\2", $val); - } - - $method = "set_$prop"; - - if ( !isset(self::$_methods_cache[$method]) ) { - self::$_methods_cache[$method] = method_exists($this, $method); - } - - if ( self::$_methods_cache[$method] ) { - $this->$method($val); - } - else { - $this->_props[$prop] = $val; - } - } - - /** - * PHP5 overloaded getter - * Along with {@link Style::__set()} __get() provides access to all CSS - * properties directly. Typically __get() is not called directly outside - * of this class. - * On each modification clear cache to return accurate setting. - * Also affects direct settings not using __set - * - * @param string $prop - * - * @throws DOMPDF_Exception - * @return mixed - */ - function __get($prop) { - if ( !isset(self::$_defaults[$prop]) ) { - throw new DOMPDF_Exception("'$prop' is not a valid CSS2 property."); - } - - if ( isset($this->_prop_cache[$prop]) && $this->_prop_cache[$prop] != null ) { - return $this->_prop_cache[$prop]; - } - - $method = "get_$prop"; - - // Fall back on defaults if property is not set - if ( !isset($this->_props[$prop]) ) { - $this->_props[$prop] = self::$_defaults[$prop]; - } - - if ( !isset(self::$_methods_cache[$method]) ) { - self::$_methods_cache[$method] = method_exists($this, $method); - } - - if ( self::$_methods_cache[$method] ) { - return $this->_prop_cache[$prop] = $this->$method(); - } - - return $this->_prop_cache[$prop] = $this->_props[$prop]; - } - - function get_font_family_raw(){ - return trim($this->_props["font_family"], " \t\n\r\x0B\"'"); - } - - /** - * Getter for the 'font-family' CSS property. - * Uses the {@link Font_Metrics} class to resolve the font family into an - * actual font file. - * - * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-family - * @throws DOMPDF_Exception - * - * @return string - */ - function get_font_family() { - if ( isset($this->_font_family) ) { - return $this->_font_family; - } - - $DEBUGCSS=DEBUGCSS; //=DEBUGCSS; Allow override of global setting for ad hoc debug - - // Select the appropriate font. First determine the subtype, then check - // the specified font-families for a candidate. - - // Resolve font-weight - $weight = $this->__get("font_weight"); - - if ( is_numeric($weight) ) { - if ( $weight < 600 ) { - $weight = "normal"; - } - else { - $weight = "bold"; - } - } - else if ( $weight === "bold" || $weight === "bolder" ) { - $weight = "bold"; - } - else { - $weight = "normal"; - } - - // Resolve font-style - $font_style = $this->__get("font_style"); - - if ( $weight === "bold" && ($font_style === "italic" || $font_style === "oblique") ) { - $subtype = "bold_italic"; - } - else if ( $weight === "bold" && $font_style !== "italic" && $font_style !== "oblique" ) { - $subtype = "bold"; - } - else if ( $weight !== "bold" && ($font_style === "italic" || $font_style === "oblique") ) { - $subtype = "italic"; - } - else { - $subtype = "normal"; - } - - // Resolve the font family - if ( $DEBUGCSS ) { - print "
[get_font_family:";
-      print '('.$this->_props["font_family"].'.'.$font_style.'.'.$this->__get("font_weight").'.'.$weight.'.'.$subtype.')';
-    }
-    
-    $families = preg_split("/\s*,\s*/", $this->_props["font_family"]);
-
-    $font = null;
-    foreach($families as $family) {
-      //remove leading and trailing string delimiters, e.g. on font names with spaces;
-      //remove leading and trailing whitespace
-      $family = trim($family, " \t\n\r\x0B\"'");
-      if ( $DEBUGCSS ) {
-        print '('.$family.')';
-      }
-      $font = Font_Metrics::get_font($family, $subtype);
-
-      if ( $font ) {
-        if ($DEBUGCSS) print '('.$font.")get_font_family]\n
"; - return $this->_font_family = $font; - } - } - - $family = null; - if ( $DEBUGCSS ) { - print '(default)'; - } - $font = Font_Metrics::get_font($family, $subtype); - - if ( $font ) { - if ( $DEBUGCSS ) print '('.$font.")get_font_family]\n"; - return$this->_font_family = $font; - } - - throw new DOMPDF_Exception("Unable to find a suitable font replacement for: '" . $this->_props["font_family"] ."'"); - - } - - /** - * Returns the resolved font size, in points - * - * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size - * @return float - */ - function get_font_size() { - - if ( $this->__font_size_calculated ) { - return $this->_props["font_size"]; - } - - if ( !isset($this->_props["font_size"]) ) { - $fs = self::$_defaults["font_size"]; - } - else { - $fs = $this->_props["font_size"]; - } - - if ( !isset($this->_parent_font_size) ) { - $this->_parent_font_size = self::$default_font_size; - } - - switch ((string)$fs) { - case "xx-small": - case "x-small": - case "small": - case "medium": - case "large": - case "x-large": - case "xx-large": - $fs = self::$default_font_size * self::$font_size_keywords[$fs]; - break; - - case "smaller": - $fs = 8/9 * $this->_parent_font_size; - break; - - case "larger": - $fs = 6/5 * $this->_parent_font_size; - break; - - default: - break; - } - - // Ensure relative sizes resolve to something - if ( ($i = mb_strpos($fs, "em")) !== false ) { - $fs = mb_substr($fs, 0, $i) * $this->_parent_font_size; - } - else if ( ($i = mb_strpos($fs, "ex")) !== false ) { - $fs = mb_substr($fs, 0, $i) * $this->_parent_font_size; - } - else { - $fs = $this->length_in_pt($fs); - } - - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["font_size"] = null; - $this->_props["font_size"] = $fs; - $this->__font_size_calculated = true; - return $this->_props["font_size"]; - - } - - /** - * @link http://www.w3.org/TR/CSS21/text.html#propdef-word-spacing - * @return float - */ - function get_word_spacing() { - if ( $this->_props["word_spacing"] === "normal" ) { - return 0; - } - - return $this->_props["word_spacing"]; - } - - /** - * @link http://www.w3.org/TR/CSS21/text.html#propdef-letter-spacing - * @return float - */ - function get_letter_spacing() { - if ( $this->_props["letter_spacing"] === "normal" ) { - return 0; - } - - return $this->_props["letter_spacing"]; - } - - /** - * @link http://www.w3.org/TR/CSS21/visudet.html#propdef-line-height - * @return float - */ - function get_line_height() { - $line_height = $this->_props["line_height"]; - - if ( $line_height === "normal" ) { - return self::$default_line_height * $this->get_font_size(); - } - - if ( is_numeric($line_height) ) { - return $this->length_in_pt( $line_height . "em", $this->get_font_size()); - } - - return $this->length_in_pt( $line_height, $this->_parent_font_size ); - } - - /** - * Returns the color as an array - * - * The array has the following format: - * array(r,g,b, "r" => r, "g" => g, "b" => b, "hex" => "#rrggbb") - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color - * @return array - */ - function get_color() { - return $this->munge_color( $this->_props["color"] ); - } - - /** - * Returns the background color as an array - * - * The returned array has the same format as {@link Style::get_color()} - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color - * @return array - */ - function get_background_color() { - return $this->munge_color( $this->_props["background_color"] ); - } - - /** - * Returns the background position as an array - * - * The returned array has the following format: - * array(x,y, "x" => x, "y" => y) - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position - * @return array - */ - function get_background_position() { - $tmp = explode(" ", $this->_props["background_position"]); - - switch ($tmp[0]) { - case "left": - $x = "0%"; - break; - - case "right": - $x = "100%"; - break; - - case "top": - $y = "0%"; - break; - - case "bottom": - $y = "100%"; - break; - - case "center": - $x = "50%"; - $y = "50%"; - break; - - default: - $x = $tmp[0]; - break; - } - - if ( isset($tmp[1]) ) { - - switch ($tmp[1]) { - case "left": - $x = "0%"; - break; - - case "right": - $x = "100%"; - break; - - case "top": - $y = "0%"; - break; - - case "bottom": - $y = "100%"; - break; - - case "center": - if ( $tmp[0] === "left" || $tmp[0] === "right" || $tmp[0] === "center" ) { - $y = "50%"; - } - else { - $x = "50%"; - } - break; - - default: - $y = $tmp[1]; - break; - } - - } - else { - $y = "50%"; - } - - if ( !isset($x) ) { - $x = "0%"; - } - - if ( !isset($y) ) { - $y = "0%"; - } - - return array( - 0 => $x, "x" => $x, - 1 => $y, "y" => $y, - ); - } - - - /** - * Returns the background as it is currently stored - * - * (currently anyway only for completeness. - * not used for further processing) - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment - * @return string - */ - function get_background_attachment() { - return $this->_props["background_attachment"]; - } - - - /** - * Returns the background_repeat as it is currently stored - * - * (currently anyway only for completeness. - * not used for further processing) - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat - * @return string - */ - function get_background_repeat() { - return $this->_props["background_repeat"]; - } - - - /** - * Returns the background as it is currently stored - * - * (currently anyway only for completeness. - * not used for further processing, but the individual get_background_xxx) - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background - * @return string - */ - function get_background() { - return $this->_props["background"]; - } - - - /**#@+ - * Returns the border color as an array - * - * See {@link Style::get_color()} - * - * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties - * @return array - */ - function get_border_top_color() { - if ( $this->_props["border_top_color"] === "" ) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["border_top_color"] = null; - $this->_props["border_top_color"] = $this->__get("color"); - } - - return $this->munge_color($this->_props["border_top_color"]); - } - - function get_border_right_color() { - if ( $this->_props["border_right_color"] === "" ) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["border_right_color"] = null; - $this->_props["border_right_color"] = $this->__get("color"); - } - - return $this->munge_color($this->_props["border_right_color"]); - } - - function get_border_bottom_color() { - if ( $this->_props["border_bottom_color"] === "" ) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["border_bottom_color"] = null; - $this->_props["border_bottom_color"] = $this->__get("color"); - } - - return $this->munge_color($this->_props["border_bottom_color"]); - } - - function get_border_left_color() { - if ( $this->_props["border_left_color"] === "" ) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["border_left_color"] = null; - $this->_props["border_left_color"] = $this->__get("color"); - } - - return $this->munge_color($this->_props["border_left_color"]); - } - - /**#@-*/ - - /**#@+ - * Returns the border width, as it is currently stored - * - * @link http://www.w3.org/TR/CSS21/box.html#border-width-properties - * @return float|string - */ - function get_border_top_width() { - $style = $this->__get("border_top_style"); - return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_top_width"]) : 0; - } - - function get_border_right_width() { - $style = $this->__get("border_right_style"); - return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_right_width"]) : 0; - } - - function get_border_bottom_width() { - $style = $this->__get("border_bottom_style"); - return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_bottom_width"]) : 0; - } - - function get_border_left_width() { - $style = $this->__get("border_left_style"); - return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_left_width"]) : 0; - } - /**#@-*/ - - /** - * Return an array of all border properties. - * - * The returned array has the following structure: - * - * array("top" => array("width" => [border-width], - * "style" => [border-style], - * "color" => [border-color (array)]), - * "bottom" ... ) - * - * - * @return array - */ - function get_border_properties() { - return array( - "top" => array( - "width" => $this->__get("border_top_width"), - "style" => $this->__get("border_top_style"), - "color" => $this->__get("border_top_color"), - ), - "bottom" => array( - "width" => $this->__get("border_bottom_width"), - "style" => $this->__get("border_bottom_style"), - "color" => $this->__get("border_bottom_color"), - ), - "right" => array( - "width" => $this->__get("border_right_width"), - "style" => $this->__get("border_right_style"), - "color" => $this->__get("border_right_color"), - ), - "left" => array( - "width" => $this->__get("border_left_width"), - "style" => $this->__get("border_left_style"), - "color" => $this->__get("border_left_color"), - ), - ); - } - - /** - * Return a single border property - * - * @param string $side - * - * @return mixed - */ - protected function _get_border($side) { - $color = $this->__get("border_" . $side . "_color"); - - return $this->__get("border_" . $side . "_width") . " " . - $this->__get("border_" . $side . "_style") . " " . $color["hex"]; - } - - /**#@+ - * Return full border properties as a string - * - * Border properties are returned just as specified in CSS: - *
[width] [style] [color]
- * e.g. "1px solid blue" - * - * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties - * @return string - */ - function get_border_top() { - return $this->_get_border("top"); - } - - function get_border_right() { - return $this->_get_border("right"); - } - - function get_border_bottom() { - return $this->_get_border("bottom"); - } - - function get_border_left() { - return $this->_get_border("left"); - } - /**#@-*/ - - function get_computed_border_radius($w, $h) { - if ( !empty($this->_computed_border_radius) ) { - return $this->_computed_border_radius; - } - - $rTL = $this->__get("border_top_left_radius"); - $rTR = $this->__get("border_top_right_radius"); - $rBL = $this->__get("border_bottom_left_radius"); - $rBR = $this->__get("border_bottom_right_radius"); - - if ( $rTL + $rTR + $rBL + $rBR == 0 ) { - return $this->_computed_border_radius = array( - 0, 0, 0, 0, - "top-left" => 0, - "top-right" => 0, - "bottom-right" => 0, - "bottom-left" => 0, - ); - } - - $t = $this->__get("border_top_width"); - $r = $this->__get("border_right_width"); - $b = $this->__get("border_bottom_width"); - $l = $this->__get("border_left_width"); - - $rTL = min($rTL, $h - $rBL - $t/2 - $b/2, $w - $rTR - $l/2 - $r/2); - $rTR = min($rTR, $h - $rBR - $t/2 - $b/2, $w - $rTL - $l/2 - $r/2); - $rBL = min($rBL, $h - $rTL - $t/2 - $b/2, $w - $rBR - $l/2 - $r/2); - $rBR = min($rBR, $h - $rTR - $t/2 - $b/2, $w - $rBL - $l/2 - $r/2); - - return $this->_computed_border_radius = array( - $rTL, $rTR, $rBR, $rBL, - "top-left" => $rTL, - "top-right" => $rTR, - "bottom-right" => $rBR, - "bottom-left" => $rBL, - ); - } - /**#@-*/ - - - /** - * Returns the outline color as an array - * - * See {@link Style::get_color()} - * - * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties - * @return array - */ - function get_outline_color() { - if ( $this->_props["outline_color"] === "" ) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["outline_color"] = null; - $this->_props["outline_color"] = $this->__get("color"); - } - - return $this->munge_color($this->_props["outline_color"]); - } - - /**#@+ - * Returns the outline width, as it is currently stored - * @return float|string - */ - function get_outline_width() { - $style = $this->__get("outline_style"); - return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["outline_width"]) : 0; - } - - /**#@+ - * Return full outline properties as a string - * - * Outline properties are returned just as specified in CSS: - *
[width] [style] [color]
- * e.g. "1px solid blue" - * - * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties - * @return string - */ - function get_outline() { - $color = $this->__get("outline_color"); - return - $this->__get("outline_width") . " " . - $this->__get("outline_style") . " " . - $color["hex"]; - } - /**#@-*/ - - /** - * Returns border spacing as an array - * - * The array has the format (h_space,v_space) - * - * @link http://www.w3.org/TR/CSS21/tables.html#propdef-border-spacing - * @return array - */ - function get_border_spacing() { - $arr = explode(" ", $this->_props["border_spacing"]); - if ( count($arr) == 1 ) { - $arr[1] = $arr[0]; - } - return $arr; - } - -/*==============================*/ - - /* - !important attribute - For basic functionality of the !important attribute with overloading - of several styles of an element, changes in inherit(), merge() and _parse_properties() - are sufficient [helpers var $_important_props, __construct(), important_set(), important_get()] - - Only for combined attributes extra treatment needed. See below. - - div { border: 1px red; } - div { border: solid; } // Not combined! Only one occurence of same style per context - // - div { border: 1px red; } - div a { border: solid; } // Adding to border style ok by inheritance - // - div { border-style: solid; } // Adding to border style ok because of different styles - div { border: 1px red; } - // - div { border-style: solid; !important} // border: overrides, even though not !important - div { border: 1px dashed red; } - // - div { border: 1px red; !important } - div a { border-style: solid; } // Need to override because not set - - Special treatment: - At individual property like border-top-width need to check whether overriding value is also !important. - Also store the !important condition for later overrides. - Since not known who is initiating the override, need to get passed !important as parameter. - !important Paramter taken as in the original style in the css file. - When property border !important given, do not mark subsets like border_style as important. Only - individual properties. - - Note: - Setting individual property directly from css with e.g. set_border_top_style() is not needed, because - missing set funcions handled by a generic handler __set(), including the !important. - Setting individual property of as sub-property is handled below. - - Implementation see at _set_style_side_type() - Callers _set_style_sides_type(), _set_style_type, _set_style_type_important() - - Related functionality for background, padding, margin, font, list_style - */ - - /* Generalized set function for individual attribute of combined style. - * With check for !important - * Applicable for background, border, padding, margin, font, list_style - * Note: $type has a leading underscore (or is empty), the others not. - */ - protected function _set_style_side_type($style, $side, $type, $val, $important) { - $prop = $style.'_'.$side.$type; - - if ( !isset($this->_important_props[$prop]) || $important) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$prop] = null; - if ( $important ) { - $this->_important_props[$prop] = true; - } - $this->_props[$prop] = $val; - } - } - - protected function _set_style_sides_type($style,$top,$right,$bottom,$left,$type,$important) { - $this->_set_style_side_type($style,'top',$type,$top,$important); - $this->_set_style_side_type($style,'right',$type,$right,$important); - $this->_set_style_side_type($style,'bottom',$type,$bottom,$important); - $this->_set_style_side_type($style,'left',$type,$left,$important); - } - - protected function _set_style_type($style,$type,$val,$important) { - $val = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces - $arr = explode(" ", $val); - - switch (count($arr)) { - case 1: $this->_set_style_sides_type($style,$arr[0],$arr[0],$arr[0],$arr[0],$type,$important); break; - case 2: $this->_set_style_sides_type($style,$arr[0],$arr[1],$arr[0],$arr[1],$type,$important); break; - case 3: $this->_set_style_sides_type($style,$arr[0],$arr[1],$arr[2],$arr[1],$type,$important); break; - case 4: $this->_set_style_sides_type($style,$arr[0],$arr[1],$arr[2],$arr[3],$type,$important); break; - } - - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$style.$type] = null; - $this->_props[$style.$type] = $val; - } - - protected function _set_style_type_important($style,$type,$val) { - $this->_set_style_type($style,$type,$val,isset($this->_important_props[$style.$type])); - } - - /* Anyway only called if _important matches and is assigned - * E.g. _set_style_side_type($style,$side,'',str_replace("none", "0px", $val),isset($this->_important_props[$style.'_'.$side])); - */ - protected function _set_style_side_width_important($style,$side,$val) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$style.'_'.$side] = null; - $this->_props[$style.'_'.$side] = str_replace("none", "0px", $val); - } - - protected function _set_style($style,$val,$important) { - if ( !isset($this->_important_props[$style]) || $important) { - if ( $important ) { - $this->_important_props[$style] = true; - } - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$style] = null; - $this->_props[$style] = $val; - } - } - - protected function _image($val) { - $DEBUGCSS=DEBUGCSS; - $parsed_url = "none"; - - if ( mb_strpos($val, "url") === false ) { - $path = "none"; //Don't resolve no image -> otherwise would prefix path and no longer recognize as none - } - else { - $val = preg_replace("/url\(['\"]?([^'\")]+)['\"]?\)/","\\1", trim($val)); - - // Resolve the url now in the context of the current stylesheet - $parsed_url = explode_url($val); - if ( $parsed_url["protocol"] == "" && $this->_stylesheet->get_protocol() == "" ) { - if ($parsed_url["path"][0] === '/' || $parsed_url["path"][0] === '\\' ) { - $path = $_SERVER["DOCUMENT_ROOT"].'/'; - } - else { - $path = $this->_stylesheet->get_base_path(); - } - - $path .= $parsed_url["path"] . $parsed_url["file"]; - $path = realpath($path); - // If realpath returns FALSE then specifically state that there is no background image - if ( !$path ) { - $path = 'none'; - } - } - else { - $path = build_url($this->_stylesheet->get_protocol(), - $this->_stylesheet->get_host(), - $this->_stylesheet->get_base_path(), - $val); - } - } - if ($DEBUGCSS) { - print "
[_image\n";
-      print_r($parsed_url);
-      print $this->_stylesheet->get_protocol()."\n".$this->_stylesheet->get_base_path()."\n".$path."\n";
-      print "_image]
";; - } - return $path; - } - -/*======================*/ - - /** - * Sets color - * - * The color parameter can be any valid CSS color value - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color - * @param string $color - */ - function set_color($color) { - $col = $this->munge_color($color); - - if ( is_null($col) || !isset($col["hex"]) ) { - $color = "inherit"; - } - else { - $color = $col["hex"]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["color"] = null; - $this->_props["color"] = $color; - } - - /** - * Sets the background color - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color - * @param string $color - */ - function set_background_color($color) { - $col = $this->munge_color($color); - - if ( is_null($col) ) { - return; - //$col = self::$_defaults["background_color"]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["background_color"] = null; - $this->_props["background_color"] = is_array($col) ? $col["hex"] : $col; - } - - /** - * Set the background image url - * @link http://www.w3.org/TR/CSS21/colors.html#background-properties - * - * @param string $val - */ - function set_background_image($val) { - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["background_image"] = null; - $this->_props["background_image"] = $this->_image($val); - } - - /** - * Sets the background repeat - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat - * @param string $val - */ - function set_background_repeat($val) { - if ( is_null($val) ) { - $val = self::$_defaults["background_repeat"]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["background_repeat"] = null; - $this->_props["background_repeat"] = $val; - } - - /** - * Sets the background attachment - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment - * @param string $val - */ - function set_background_attachment($val) { - if ( is_null($val) ) { - $val = self::$_defaults["background_attachment"]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["background_attachment"] = null; - $this->_props["background_attachment"] = $val; - } - - /** - * Sets the background position - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position - * @param string $val - */ - function set_background_position($val) { - if ( is_null($val) ) { - $val = self::$_defaults["background_position"]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["background_position"] = null; - $this->_props["background_position"] = $val; - } - - /** - * Sets the background - combined options - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background - * @param string $val - */ - function set_background($val) { - $val = trim($val); - $important = isset($this->_important_props["background"]); - - if ( $val === "none" ) { - $this->_set_style("background_image", "none", $important); - $this->_set_style("background_color", "transparent", $important); - } - else { - $pos = array(); - $tmp = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces - $tmp = preg_split("/\s+/", $tmp); - - foreach($tmp as $attr) { - if ( mb_substr($attr, 0, 3) === "url" || $attr === "none" ) { - $this->_set_style("background_image", $this->_image($attr), $important); - } - elseif ( $attr === "fixed" || $attr === "scroll" ) { - $this->_set_style("background_attachment", $attr, $important); - } - elseif ( $attr === "repeat" || $attr === "repeat-x" || $attr === "repeat-y" || $attr === "no-repeat" ) { - $this->_set_style("background_repeat", $attr, $important); - } - elseif ( ($col = $this->munge_color($attr)) != null ) { - $this->_set_style("background_color", is_array($col) ? $col["hex"] : $col, $important); - } - else { - $pos[] = $attr; - } - } - - if (count($pos)) { - $this->_set_style("background_position", implode(" ", $pos), $important); - } - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["background"] = null; - $this->_props["background"] = $val; - } - - /** - * Sets the font size - * - * $size can be any acceptable CSS size - * - * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size - * @param string|float $size - */ - function set_font_size($size) { - $this->__font_size_calculated = false; - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["font_size"] = null; - $this->_props["font_size"] = $size; - } - - /** - * Sets the font style - * - * combined attributes - * set individual attributes also, respecting !important mark - * exactly this order, separate by space. Multiple fonts separated by comma: - * font-style, font-variant, font-weight, font-size, line-height, font-family - * - * Other than with border and list, existing partial attributes should - * reset when starting here, even when not mentioned. - * If individual attribute is !important and explicite or implicite replacement is not, - * keep individual attribute - * - * require whitespace as delimiters for single value attributes - * On delimiter "/" treat first as font height, second as line height - * treat all remaining at the end of line as font - * font-style, font-variant, font-weight, font-size, line-height, font-family - * - * missing font-size and font-family might be not allowed, but accept it here and - * use default (medium size, enpty font name) - * - * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style - * @param $val - */ - function set_font($val) { - $this->__font_size_calculated = false; - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["font"] = null; - $this->_props["font"] = $val; - - $important = isset($this->_important_props["font"]); - - if ( preg_match("/^(italic|oblique|normal)\s*(.*)$/i",$val,$match) ) { - $this->_set_style("font_style", $match[1], $important); - $val = $match[2]; - } - else { - $this->_set_style("font_style", self::$_defaults["font_style"], $important); - } - - if ( preg_match("/^(small-caps|normal)\s*(.*)$/i",$val,$match) ) { - $this->_set_style("font_variant", $match[1], $important); - $val = $match[2]; - } - else { - $this->_set_style("font_variant", self::$_defaults["font_variant"], $important); - } - - //matching numeric value followed by unit -> this is indeed a subsequent font size. Skip! - if ( preg_match("/^(bold|bolder|lighter|100|200|300|400|500|600|700|800|900|normal)\s*(.*)$/i", $val, $match) && - !preg_match("/^(?:pt|px|pc|em|ex|in|cm|mm|%)/",$match[2]) - ) { - $this->_set_style("font_weight", $match[1], $important); - $val = $match[2]; - } - else { - $this->_set_style("font_weight", self::$_defaults["font_weight"], $important); - } - - if ( preg_match("/^(xx-small|x-small|small|medium|large|x-large|xx-large|smaller|larger|\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))\s*(.*)$/i",$val,$match) ) { - $this->_set_style("font_size", $match[1], $important); - $val = $match[2]; - if ( preg_match("/^\/\s*(\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))\s*(.*)$/i", $val, $match ) ) { - $this->_set_style("line_height", $match[1], $important); - $val = $match[2]; - } - else { - $this->_set_style("line_height", self::$_defaults["line_height"], $important); - } - } - else { - $this->_set_style("font_size", self::$_defaults["font_size"], $important); - $this->_set_style("line_height", self::$_defaults["line_height"], $important); - } - - if( strlen($val) != 0 ) { - $this->_set_style("font_family", $val, $important); - } - else { - $this->_set_style("font_family", self::$_defaults["font_family"], $important); - } - } - - /**#@+ - * Sets page break properties - * - * @link http://www.w3.org/TR/CSS21/page.html#page-breaks - * @param string $break - */ - function set_page_break_before($break) { - if ( $break === "left" || $break === "right" ) { - $break = "always"; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["page_break_before"] = null; - $this->_props["page_break_before"] = $break; - } - - function set_page_break_after($break) { - if ( $break === "left" || $break === "right" ) { - $break = "always"; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["page_break_after"] = null; - $this->_props["page_break_after"] = $break; - } - /**#@-*/ - - //........................................................................ - - /**#@+ - * Sets the margin size - * - * @link http://www.w3.org/TR/CSS21/box.html#margin-properties - * @param $val - */ - function set_margin_top($val) { - $this->_set_style_side_width_important('margin','top',$val); - } - - function set_margin_right($val) { - $this->_set_style_side_width_important('margin','right',$val); - } - - function set_margin_bottom($val) { - $this->_set_style_side_width_important('margin','bottom',$val); - } - - function set_margin_left($val) { - $this->_set_style_side_width_important('margin','left',$val); - } - - function set_margin($val) { - $val = str_replace("none", "0px", $val); - $this->_set_style_type_important('margin','',$val); - } - /**#@-*/ - - /**#@+ - * Sets the padding size - * - * @link http://www.w3.org/TR/CSS21/box.html#padding-properties - * @param $val - */ - function set_padding_top($val) { - $this->_set_style_side_width_important('padding','top',$val); - } - - function set_padding_right($val) { - $this->_set_style_side_width_important('padding','right',$val); - } - - function set_padding_bottom($val) { - $this->_set_style_side_width_important('padding','bottom',$val); - } - - function set_padding_left($val) { - $this->_set_style_side_width_important('padding','left',$val); - } - - function set_padding($val) { - $val = str_replace("none", "0px", $val); - $this->_set_style_type_important('padding','',$val); - } - /**#@-*/ - - /** - * Sets a single border - * - * @param string $side - * @param string $border_spec ([width] [style] [color]) - * @param boolean $important - */ - protected function _set_border($side, $border_spec, $important) { - $border_spec = preg_replace("/\s*\,\s*/", ",", $border_spec); - //$border_spec = str_replace(",", " ", $border_spec); // Why did we have this ?? rbg(10, 102, 10) > rgb(10 102 10) - $arr = explode(" ", $border_spec); - - // FIXME: handle partial values - - //For consistency of individal and combined properties, and with ie8 and firefox3 - //reset all attributes, even if only partially given - $this->_set_style_side_type('border',$side,'_style',self::$_defaults['border_'.$side.'_style'],$important); - $this->_set_style_side_type('border',$side,'_width',self::$_defaults['border_'.$side.'_width'],$important); - $this->_set_style_side_type('border',$side,'_color',self::$_defaults['border_'.$side.'_color'],$important); - - foreach ($arr as $value) { - $value = trim($value); - if ( in_array($value, self::$BORDER_STYLES) ) { - $this->_set_style_side_type('border',$side,'_style',$value,$important); - } - else if ( preg_match("/[.0-9]+(?:px|pt|pc|em|ex|%|in|mm|cm)|(?:thin|medium|thick)/", $value ) ) { - $this->_set_style_side_type('border',$side,'_width',$value,$important); - } - else { - // must be color - $this->_set_style_side_type('border',$side,'_color',$value,$important); - } - } - - //see __set and __get, on all assignments clear cache! - $this->_prop_cache['border_'.$side] = null; - $this->_props['border_'.$side] = $border_spec; - } - - /** - * Sets the border styles - * - * @link http://www.w3.org/TR/CSS21/box.html#border-properties - * @param string $val - */ - function set_border_top($val) { - $this->_set_border("top", $val, isset($this->_important_props['border_top'])); - } - - function set_border_right($val) { - $this->_set_border("right", $val, isset($this->_important_props['border_right'])); - } - - function set_border_bottom($val) { - $this->_set_border("bottom", $val, isset($this->_important_props['border_bottom'])); - } - - function set_border_left($val) { - $this->_set_border("left", $val, isset($this->_important_props['border_left'])); - } - - function set_border($val) { - $important = isset($this->_important_props["border"]); - $this->_set_border("top", $val, $important); - $this->_set_border("right", $val, $important); - $this->_set_border("bottom", $val, $important); - $this->_set_border("left", $val, $important); - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["border"] = null; - $this->_props["border"] = $val; - } - - function set_border_width($val) { - $this->_set_style_type_important('border','_width',$val); - } - - function set_border_color($val) { - $this->_set_style_type_important('border','_color',$val); - } - - function set_border_style($val) { - $this->_set_style_type_important('border','_style',$val); - } - - /** - * Sets the border radius size - * - * http://www.w3.org/TR/css3-background/#corners - */ - function set_border_top_left_radius($val) { - $this->_set_border_radius_corner($val, "top_left"); - } - - function set_border_top_right_radius($val) { - $this->_set_border_radius_corner($val, "top_right"); - } - - function set_border_bottom_left_radius($val) { - $this->_set_border_radius_corner($val, "bottom_left"); - } - - function set_border_bottom_right_radius($val) { - $this->_set_border_radius_corner($val, "bottom_right"); - } - - function set_border_radius($val) { - $val = preg_replace("/\s*\,\s*/", ",", $val); // when border-radius has spaces - $arr = explode(" ", $val); - - switch (count($arr)) { - case 1: $this->_set_border_radii($arr[0],$arr[0],$arr[0],$arr[0]); break; - case 2: $this->_set_border_radii($arr[0],$arr[1],$arr[0],$arr[1]); break; - case 3: $this->_set_border_radii($arr[0],$arr[1],$arr[2],$arr[1]); break; - case 4: $this->_set_border_radii($arr[0],$arr[1],$arr[2],$arr[3]); break; - } - } - - protected function _set_border_radii($val1, $val2, $val3, $val4) { - $this->_set_border_radius_corner($val1, "top_left"); - $this->_set_border_radius_corner($val2, "top_right"); - $this->_set_border_radius_corner($val3, "bottom_right"); - $this->_set_border_radius_corner($val4, "bottom_left"); - } - - protected function _set_border_radius_corner($val, $corner) { - $this->_has_border_radius = true; - - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["border_" . $corner . "_radius"] = null; - - $this->_props["border_" . $corner . "_radius"] = $this->length_in_pt($val); - } - - /** - * Sets the outline styles - * - * @link http://www.w3.org/TR/CSS21/ui.html#dynamic-outlines - * @param string $val - */ - function set_outline($val) { - $important = isset($this->_important_props["outline"]); - - $props = array( - "outline_style", - "outline_width", - "outline_color", - ); - - foreach($props as $prop) { - $_val = self::$_defaults[$prop]; - - if ( !isset($this->_important_props[$prop]) || $important) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$prop] = null; - if ( $important ) { - $this->_important_props[$prop] = true; - } - $this->_props[$prop] = $_val; - } - } - - $val = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces - $arr = explode(" ", $val); - foreach ($arr as $value) { - $value = trim($value); - - if ( in_array($value, self::$BORDER_STYLES) ) { - $this->set_outline_style($value); - } - else if ( preg_match("/[.0-9]+(?:px|pt|pc|em|ex|%|in|mm|cm)|(?:thin|medium|thick)/", $value ) ) { - $this->set_outline_width($value); - } - else { - // must be color - $this->set_outline_color($value); - } - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["outline"] = null; - $this->_props["outline"] = $val; - } - - function set_outline_width($val) { - $this->_set_style_type_important('outline','_width',$val); - } - - function set_outline_color($val) { - $this->_set_style_type_important('outline','_color',$val); - } - - function set_outline_style($val) { - $this->_set_style_type_important('outline','_style',$val); - } - - /** - * Sets the border spacing - * - * @link http://www.w3.org/TR/CSS21/box.html#border-properties - * @param float $val - */ - function set_border_spacing($val) { - $arr = explode(" ", $val); - - if ( count($arr) == 1 ) { - $arr[1] = $arr[0]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["border_spacing"] = null; - $this->_props["border_spacing"] = "$arr[0] $arr[1]"; - } - - /** - * Sets the list style image - * - * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image - * @param $val - */ - function set_list_style_image($val) { - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["list_style_image"] = null; - $this->_props["list_style_image"] = $this->_image($val); - } - - /** - * Sets the list style - * - * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style - * @param $val - */ - function set_list_style($val) { - $important = isset($this->_important_props["list_style"]); - $arr = explode(" ", str_replace(",", " ", $val)); - - static $types = array( - "disc", "circle", "square", - "decimal-leading-zero", "decimal", "1", - "lower-roman", "upper-roman", "a", "A", - "lower-greek", - "lower-latin", "upper-latin", - "lower-alpha", "upper-alpha", - "armenian", "georgian", "hebrew", - "cjk-ideographic", "hiragana", "katakana", - "hiragana-iroha", "katakana-iroha", "none" - ); - - static $positions = array("inside", "outside"); - - foreach ($arr as $value) { - /* http://www.w3.org/TR/CSS21/generate.html#list-style - * A value of 'none' for the 'list-style' property sets both 'list-style-type' and 'list-style-image' to 'none' - */ - if ( $value === "none" ) { - $this->_set_style("list_style_type", $value, $important); - $this->_set_style("list_style_image", $value, $important); - continue; - } - - //On setting or merging or inheriting list_style_image as well as list_style_type, - //and url exists, then url has precedence, otherwise fall back to list_style_type - //Firefox is wrong here (list_style_image gets overwritten on explicite list_style_type) - //Internet Explorer 7/8 and dompdf is right. - - if ( mb_substr($value, 0, 3) === "url" ) { - $this->_set_style("list_style_image", $this->_image($value), $important); - continue; - } - - if ( in_array($value, $types) ) { - $this->_set_style("list_style_type", $value, $important); - } - else if ( in_array($value, $positions) ) { - $this->_set_style("list_style_position", $value, $important); - } - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["list_style"] = null; - $this->_props["list_style"] = $val; - } - - function set_size($val) { - $length_re = "/(\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))/"; - - $val = mb_strtolower($val); - - if ( $val === "auto" ) { - return; - } - - $parts = preg_split("/\s+/", $val); - - $computed = array(); - if ( preg_match($length_re, $parts[0]) ) { - $computed[] = $this->length_in_pt($parts[0]); - - if ( isset($parts[1]) && preg_match($length_re, $parts[1]) ) { - $computed[] = $this->length_in_pt($parts[1]); - } - else { - $computed[] = $computed[0]; - } - } - elseif ( isset(CPDF_Adapter::$PAPER_SIZES[$parts[0]]) ) { - $computed = array_slice(CPDF_Adapter::$PAPER_SIZES[$parts[0]], 2, 2); - - if ( isset($parts[1]) && $parts[1] === "landscape" ) { - $computed = array_reverse($computed); - } - } - else { - return; - } - - $this->_props["size"] = $computed; - } - - /** - * Sets the CSS3 transform property - * - * @link http://www.w3.org/TR/css3-2d-transforms/#transform-property - * @param string $val - */ - function set_transform($val) { - $number = "\s*([^,\s]+)\s*"; - $tr_value = "\s*([^,\s]+)\s*"; - $angle = "\s*([^,\s]+(?:deg|rad)?)\s*"; - - if ( !preg_match_all("/[a-z]+\([^\)]+\)/i", $val, $parts, PREG_SET_ORDER) ) { - return; - } - - $functions = array( - //"matrix" => "\($number,$number,$number,$number,$number,$number\)", - - "translate" => "\($tr_value(?:,$tr_value)?\)", - "translateX" => "\($tr_value\)", - "translateY" => "\($tr_value\)", - - "scale" => "\($number(?:,$number)?\)", - "scaleX" => "\($number\)", - "scaleY" => "\($number\)", - - "rotate" => "\($angle\)", - - "skew" => "\($angle(?:,$angle)?\)", - "skewX" => "\($angle\)", - "skewY" => "\($angle\)", - ); - - $transforms = array(); - - foreach($parts as $part) { - $t = $part[0]; - - foreach($functions as $name => $pattern) { - if ( preg_match("/$name\s*$pattern/i", $t, $matches) ) { - $values = array_slice($matches, 1); - - switch($name) { - // units - case "rotate": - case "skew": - case "skewX": - case "skewY": - - foreach($values as $i => $value) { - if ( strpos($value, "rad") ) { - $values[$i] = rad2deg(floatval($value)); - } - else { - $values[$i] = floatval($value); - } - } - - switch($name) { - case "skew": - if ( !isset($values[1]) ) { - $values[1] = 0; - } - break; - case "skewX": - $name = "skew"; - $values = array($values[0], 0); - break; - case "skewY": - $name = "skew"; - $values = array(0, $values[0]); - break; - } - break; - - // units - case "translate": - $values[0] = $this->length_in_pt($values[0], $this->width); - - if ( isset($values[1]) ) { - $values[1] = $this->length_in_pt($values[1], $this->height); - } - else { - $values[1] = 0; - } - break; - - case "translateX": - $name = "translate"; - $values = array($this->length_in_pt($values[0], $this->width), 0); - break; - - case "translateY": - $name = "translate"; - $values = array(0, $this->length_in_pt($values[0], $this->height)); - break; - - // units - case "scale": - if ( !isset($values[1]) ) { - $values[1] = $values[0]; - } - break; - - case "scaleX": - $name = "scale"; - $values = array($values[0], 1.0); - break; - - case "scaleY": - $name = "scale"; - $values = array(1.0, $values[0]); - break; - } - - $transforms[] = array( - $name, - $values, - ); - } - } - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["transform"] = null; - $this->_props["transform"] = $transforms; - } - - function set__webkit_transform($val) { - $this->set_transform($val); - } - - function set__webkit_transform_origin($val) { - $this->set_transform_origin($val); - } - - /** - * Sets the CSS3 transform-origin property - * - * @link http://www.w3.org/TR/css3-2d-transforms/#transform-origin - * @param string $val - */ - function set_transform_origin($val) { - $values = preg_split("/\s+/", $val); - - if ( count($values) === 0) { - return; - } - - foreach($values as &$value) { - if ( in_array($value, array("top", "left")) ) { - $value = 0; - } - - if ( in_array($value, array("bottom", "right")) ) { - $value = "100%"; - } - } - - if ( !isset($values[1]) ) { - $values[1] = $values[0]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["transform_origin"] = null; - $this->_props["transform_origin"] = $values; - } - - protected function parse_image_resolution($val) { - // If exif data could be get: - // $re = '/^\s*(\d+|normal|auto)(?:\s*,\s*(\d+|normal))?\s*$/'; - - $re = '/^\s*(\d+|normal|auto)\s*$/'; - - if ( !preg_match($re, $val, $matches) ) { - return null; - } - - return $matches[1]; - } - - // auto | normal | dpi - function set_background_image_resolution($val) { - $parsed = $this->parse_image_resolution($val); - - $this->_prop_cache["background_image_resolution"] = null; - $this->_props["background_image_resolution"] = $parsed; - } - - // auto | normal | dpi - function set_image_resolution($val) { - $parsed = $this->parse_image_resolution($val); - - $this->_prop_cache["image_resolution"] = null; - $this->_props["image_resolution"] = $parsed; - } - - function set__dompdf_background_image_resolution($val) { - $this->set_background_image_resolution($val); - } - - function set__dompdf_image_resolution($val) { - $this->set_image_resolution($val); - } - - function set_z_index($val) { - if ( round($val) != $val && $val !== "auto" ) { - return; - } - - $this->_prop_cache["z_index"] = null; - $this->_props["z_index"] = $val; - } - - function set_counter_increment($val) { - $val = trim($val); - $value = null; - - if ( in_array($val, array("none", "inherit")) ) { - $value = $val; - } - else { - if ( preg_match_all("/(".self::CSS_IDENTIFIER.")(?:\s+(".self::CSS_INTEGER."))?/", $val, $matches, PREG_SET_ORDER) ){ - $value = array(); - foreach($matches as $match) { - $value[$match[1]] = isset($match[2]) ? $match[2] : 1; - } - } - } - - $this->_prop_cache["counter_increment"] = null; - $this->_props["counter_increment"] = $value; - } - - /** - * Generate a string representation of the Style - * - * This dumps the entire property array into a string via print_r. Useful - * for debugging. - * - * @return string - */ - /*DEBUGCSS print: see below additional debugging util*/ - function __toString() { - return print_r(array_merge(array("parent_font_size" => $this->_parent_font_size), - $this->_props), true); - } - -/*DEBUGCSS*/ function debug_print() { -/*DEBUGCSS*/ print "parent_font_size:".$this->_parent_font_size . ";\n"; -/*DEBUGCSS*/ foreach($this->_props as $prop => $val ) { -/*DEBUGCSS*/ print $prop.':'.$val; -/*DEBUGCSS*/ if (isset($this->_important_props[$prop])) { -/*DEBUGCSS*/ print '!important'; -/*DEBUGCSS*/ } -/*DEBUGCSS*/ print ";\n"; -/*DEBUGCSS*/ } -/*DEBUGCSS*/ } -} diff --git a/library/vendor/dompdf/include/stylesheet.cls.php b/library/vendor/dompdf/include/stylesheet.cls.php deleted file mode 100644 index a74956754..000000000 --- a/library/vendor/dompdf/include/stylesheet.cls.php +++ /dev/null @@ -1,1418 +0,0 @@ - - * @author Helmut Tischer - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * The location of the default built-in CSS file. - * {@link Stylesheet::DEFAULT_STYLESHEET} - */ -define('__DEFAULT_STYLESHEET', DOMPDF_LIB_DIR . DIRECTORY_SEPARATOR . "res" . DIRECTORY_SEPARATOR . "html.css"); - -/** - * The master stylesheet class - * - * The Stylesheet class is responsible for parsing stylesheets and style - * tags/attributes. It also acts as a registry of the individual Style - * objects generated by the current set of loaded CSS files and style - * elements. - * - * @see Style - * @package dompdf - */ -class Stylesheet { - - /** - * The location of the default built-in CSS file. - */ - const DEFAULT_STYLESHEET = __DEFAULT_STYLESHEET; - - /** - * User agent stylesheet origin - * - * @var int - */ - const ORIG_UA = 1; - - /** - * User normal stylesheet origin - * - * @var int - */ - const ORIG_USER = 2; - - /** - * Author normal stylesheet origin - * - * @var int - */ - const ORIG_AUTHOR = 3; - - private static $_stylesheet_origins = array( - self::ORIG_UA => -0x0FFFFFFF, // user agent style sheets - self::ORIG_USER => -0x0000FFFF, // user normal style sheets - self::ORIG_AUTHOR => 0x00000000, // author normal style sheets - ); - - /** - * Current dompdf instance - * - * @var DOMPDF - */ - private $_dompdf; - - /** - * Array of currently defined styles - * - * @var Style[] - */ - private $_styles; - - /** - * Base protocol of the document being parsed - * Used to handle relative urls. - * - * @var string - */ - private $_protocol; - - /** - * Base hostname of the document being parsed - * Used to handle relative urls. - * - * @var string - */ - private $_base_host; - - /** - * Base path of the document being parsed - * Used to handle relative urls. - * - * @var string - */ - private $_base_path; - - /** - * The styles defined by @page rules - * - * @var array + $child = $child->nextSibling; + } + } else { + $css = $tag->nodeValue; + } + + $this->css->load_css($css); + break; + } + } + } + + /** + * @param string $cacheId + * @deprecated + */ + public function enable_caching($cacheId) + { + $this->enableCaching($cacheId); + } + + /** + * Enable experimental caching capability + * + * @param string $cacheId + */ + public function enableCaching($cacheId) + { + $this->cacheId = $cacheId; + } + + /** + * @param string $value + * @return bool + * @deprecated + */ + public function parse_default_view($value) + { + return $this->parseDefaultView($value); + } + + /** + * @param string $value + * @return bool + */ + public function parseDefaultView($value) + { + $valid = array("XYZ", "Fit", "FitH", "FitV", "FitR", "FitB", "FitBH", "FitBV"); + + $options = preg_split("/\s*,\s*/", trim($value)); + $defaultView = array_shift($options); + + if (!in_array($defaultView, $valid)) { + return false; + } + + $this->setDefaultView($defaultView, $options); + return true; + } + + /** + * Renders the HTML to PDF + */ + public function render() + { + $this->saveLocale(); + + $logOutputFile = $this->options->getLogOutputFile(); + if ($logOutputFile) { + if (!file_exists($logOutputFile) && is_writable(dirname($logOutputFile))) { + touch($logOutputFile); + } + + $this->startTime = microtime(true); + ob_start(); + } + + $this->processHtml(); + + $this->css->apply_styles($this->tree); + + // @page style rules : size, margins + $pageStyles = $this->css->get_page_styles(); + + $basePageStyle = $pageStyles["base"]; + unset($pageStyles["base"]); + + foreach ($pageStyles as $pageStyle) { + $pageStyle->inherit($basePageStyle); + } + + if (is_array($basePageStyle->size)) { + $this->setPaper(array(0, 0, $basePageStyle->size[0], $basePageStyle->size[1])); + } + + //TODO: We really shouldn't be doing this; properties were already set in the constructor. We should add Canvas methods to set the page size and orientation after instantiaion (see #1059). + $this->setCanvas(CanvasFactory::get_instance($this, $this->paperSize, $this->paperOrientation)); + $this->setFontMetrics(new FontMetrics($this->pdf, $this->getOptions())); + + if ($this->options->isFontSubsettingEnabled() && $this->pdf instanceof CPDF) { + foreach ($this->tree->get_frames() as $frame) { + $style = $frame->get_style(); + $node = $frame->get_node(); + + // Handle text nodes + if ($node->nodeName === "#text") { + $this->getCanvas()->register_string_subset($style->font_family, $node->nodeValue); + continue; + } + + // Handle generated content (list items) + if ($style->display === "list-item") { + $chars = ListBullet::get_counter_chars($style->list_style_type); + $this->getCanvas()->register_string_subset($style->font_family, $chars); + continue; + } + + // Handle other generated content (pseudo elements) + // FIXME: This only captures the text of the stylesheet declaration, + // not the actual generated content, and forces all possible counter + // values. See notes in issue #750. + if ($frame->get_node()->nodeName == "dompdf_generated") { + // all possible counter values + $chars = ListBullet::get_counter_chars('decimal'); + $this->getCanvas()->register_string_subset($style->font_family, $chars); + $chars = ListBullet::get_counter_chars('upper-alpha'); + $this->getCanvas()->register_string_subset($style->font_family, $chars); + $chars = ListBullet::get_counter_chars('lower-alpha'); + $this->getCanvas()->register_string_subset($style->font_family, $chars); + $chars = ListBullet::get_counter_chars('lower-greek'); + $this->getCanvas()->register_string_subset($style->font_family, $chars); + // the text of the stylesheet declaration + $this->getCanvas()->register_string_subset($style->font_family, $style->content); + continue; + } + } + } + + $root = null; + + foreach ($this->tree->get_frames() as $frame) { + // Set up the root frame + if (is_null($root)) { + $root = Factory::decorate_root($this->tree->get_root(), $this); + continue; + } + + // Create the appropriate decorators, reflowers & positioners. + Factory::decorate_frame($frame, $this, $root); + } + + // Add meta information + $title = $this->dom->getElementsByTagName("title"); + if ($title->length) { + $this->getCanvas()->add_info("Title", trim($title->item(0)->nodeValue)); + } + + $metas = $this->dom->getElementsByTagName("meta"); + $labels = array( + "author" => "Author", + "keywords" => "Keywords", + "description" => "Subject", + ); + foreach ($metas as $meta) { + $name = mb_strtolower($meta->getAttribute("name")); + $value = trim($meta->getAttribute("content")); + + if (isset($labels[$name])) { + $this->pdf->add_info($labels[$name], $value); + continue; + } + + if ($name === "dompdf.view" && $this->parseDefaultView($value)) { + $this->getCanvas()->set_default_view($this->defaultView, $this->defaultViewOptions); + } + } + + $root->set_containing_block(0, 0, $this->getCanvas()->get_width(), $this->getCanvas()->get_height()); + $root->set_renderer(new Renderer($this)); + + // This is where the magic happens: + $root->reflow(); + + // Clean up cached images + Cache::clear(); + + global $_dompdf_warnings, $_dompdf_show_warnings; + if ($_dompdf_show_warnings && isset($_dompdf_warnings)) { + echo 'Dompdf Warnings
';
+            foreach ($_dompdf_warnings as $msg) {
+                echo $msg . "\n";
+            }
+            echo $this->getCanvas()->get_cpdf()->messages;
+            echo '
'; + flush(); + } + + $this->restoreLocale(); + } + + /** + * Add meta information to the PDF after rendering + */ + public function add_info($label, $value) + { + if (!is_null($this->pdf)) { + $this->pdf->add_info($label, $value); + } + } + + /** + * Writes the output buffer in the log file + * + * @return void + */ + private function write_log() + { + $log_output_file = $this->get_option("log_output_file"); + if (!$log_output_file || !is_writable($log_output_file)) { + return; + } + + $frames = Frame::$ID_COUNTER; + $memory = memory_get_peak_usage(true) / 1024; + $time = (microtime(true) - $this->startTime) * 1000; + + $out = sprintf( + "%6d" . + "%10.2f KB" . + "%10.2f ms" . + " " . + ($this->quirksmode ? " ON" : "OFF") . + "
", $frames, $memory, $time); + + $out .= ob_get_clean(); + + $log_output_file = $this->get_option("log_output_file"); + file_put_contents($log_output_file, $out); + } + + /** + * Streams the PDF to the client + * + * The file will open a download dialog by default. The options + * parameter controls the output. Accepted options (array keys) are: + * + * 'Accept-Ranges' => 1 or 0 (=default): Send an 'Accept-Ranges:' + * HTTP header, see https://tools.ietf.org/html/rfc2616#section-14.5 + * This header seems to have caused some problems, despite the fact + * that it is supposed to solve them, so I am leaving it off by default. + * + * 'compress' = > 1 (=default) or 0: + * Apply content stream compression + * + * 'Attachment' => 1 (=default) or 0: + * Set the 'Content-Disposition:' HTTP header to 'attachment' + * (thereby causing the browser to open a download dialog) + * + * @param string $filename the name of the streamed file + * @param array $options header options (see above) + */ + public function stream($filename = 'document.pdf', $options = null) + { + $this->saveLocale(); + + $this->write_log(); + + if (!is_null($this->pdf)) { + $this->pdf->stream($filename, $options); + } + + $this->restoreLocale(); + } + + /** + * Returns the PDF as a string + * + * The file will open a download dialog by default. The options + * parameter controls the output. Accepted options are: + * + * + * 'compress' = > 1 or 0 - apply content stream compression, this is + * on (1) by default + * + * + * @param array $options options (see above) + * + * @return string + */ + public function output($options = null) + { + $this->saveLocale(); + + $this->write_log(); + + if (is_null($this->pdf)) { + return null; + } + + $output = $this->pdf->output($options); + + $this->restoreLocale(); + + return $output; + } + + /** + * @return string + * @deprecated + */ + public function output_html() + { + return $this->outputHtml(); + } + + /** + * Returns the underlying HTML document as a string + * + * @return string + */ + public function outputHtml() + { + return $this->dom->saveHTML(); + } + + /** + * Get the dompdf option value + * + * @param string $key + * @return mixed + * @deprecated + */ + public function get_option($key) + { + return $this->options->get($key); + } + + /** + * @param string $key + * @param mixed $value + * @return $this + * @deprecated + */ + public function set_option($key, $value) + { + $this->options->set($key, $value); + return $this; + } + + /** + * @param array $options + * @return $this + * @deprecated + */ + public function set_options(array $options) + { + $this->options->set($options); + return $this; + } + + /** + * @param string $size + * @param string $orientation + * @deprecated + */ + public function set_paper($size, $orientation = "portrait") + { + $this->setPaper($size, $orientation); + } + + /** + * Sets the paper size & orientation + * + * @param string $size 'letter', 'legal', 'A4', etc. {@link Dompdf\Adapter\CPDF::$PAPER_SIZES} + * @param string $orientation 'portrait' or 'landscape' + * @return $this + */ + public function setPaper($size, $orientation = "portrait") + { + $this->paperSize = $size; + $this->paperOrientation = $orientation; + return $this; + } + + /** + * @param FrameTree $tree + * @return $this + */ + public function setTree(FrameTree $tree) + { + $this->tree = $tree; + return $this; + } + + /** + * @return FrameTree + * @deprecated + */ + public function get_tree() + { + return $this->getTree(); + } + + /** + * Returns the underlying {@link FrameTree} object + * + * @return FrameTree + */ + public function getTree() + { + return $this->tree; + } + + /** + * @param string $protocol + * @return $this + * @deprecated + */ + public function set_protocol($protocol) + { + return $this->setProtocol($protocol); + } + + /** + * Sets the protocol to use + * FIXME validate these + * + * @param string $protocol + * @return $this + */ + public function setProtocol($protocol) + { + $this->protocol = $protocol; + return $this; + } + + /** + * @return string + * @deprecated + */ + public function get_protocol() + { + return $this->getProtocol(); + } + + /** + * Returns the protocol in use + * + * @return string + */ + public function getProtocol() + { + return $this->protocol; + } + + /** + * @param string $host + * @deprecated + */ + public function set_host($host) + { + $this->setBaseHost($host); + } + + /** + * Sets the base hostname + * + * @param string $baseHost + * @return $this + */ + public function setBaseHost($baseHost) + { + $this->baseHost = $baseHost; + return $this; + } + + /** + * @return string + * @deprecated + */ + public function get_host() + { + return $this->getBaseHost(); + } + + /** + * Returns the base hostname + * + * @return string + */ + public function getBaseHost() + { + return $this->baseHost; + } + + /** + * Sets the base path + * + * @param string $path + * @deprecated + */ + public function set_base_path($path) + { + $this->setBasePath($path); + } + + /** + * Sets the base path + * + * @param string $basePath + * @return $this + */ + public function setBasePath($basePath) + { + $this->basePath = $basePath; + return $this; + } + + /** + * @return string + * @deprecated + */ + public function get_base_path() + { + return $this->getBasePath(); + } + + /** + * Returns the base path + * + * @return string + */ + public function getBasePath() + { + return $this->basePath; + } + + /** + * @param string $default_view The default document view + * @param array $options The view's options + * @return $this + * @deprecated + */ + public function set_default_view($default_view, $options) + { + return $this->setDefaultView($default_view, $options); + } + + /** + * Sets the default view + * + * @param string $defaultView The default document view + * @param array $options The view's options + * @return $this + */ + public function setDefaultView($defaultView, $options) + { + $this->defaultView = $defaultView; + $this->defaultViewOptions = $options; + return $this; + } + + /** + * @param resource $http_context + * @return $this + * @deprecated + */ + public function set_http_context($http_context) + { + return $this->setHttpContext($http_context); + } + + /** + * Sets the HTTP context + * + * @param resource $httpContext + * @return $this + */ + public function setHttpContext($httpContext) + { + $this->httpContext = $httpContext; + return $this; + } + + /** + * @return resource + * @deprecated + */ + public function get_http_context() + { + return $this->getHttpContext(); + } + + /** + * Returns the HTTP context + * + * @return resource + */ + public function getHttpContext() + { + return $this->httpContext; + } + + /** + * @param Canvas $canvas + * @return $this + */ + public function setCanvas(Canvas $canvas) + { + $this->pdf = $canvas; + $this->canvas = $canvas; + return $this; + } + + /** + * @return Canvas + * @deprecated + */ + public function get_canvas() + { + return $this->getCanvas(); + } + + /** + * Return the underlying Canvas instance (e.g. Dompdf\Adapter\CPDF, Dompdf\Adapter\GD) + * + * @return Canvas + */ + public function getCanvas() + { + if (null === $this->canvas && null !== $this->pdf) { + return $this->pdf; + } + return $this->canvas; + } + + /** + * @param Stylesheet $css + * @return $this + */ + public function setCss(Stylesheet $css) + { + $this->css = $css; + return $this; + } + + /** + * @return Stylesheet + * @deprecated + */ + public function get_css() + { + return $this->getCss(); + } + + /** + * Returns the stylesheet + * + * @return Stylesheet + */ + public function getCss() + { + return $this->css; + } + + /** + * @param DOMDocument $dom + * @return $this + */ + public function setDom(DOMDocument $dom) + { + $this->dom = $dom; + return $this; + } + + /** + * @return DOMDocument + * @deprecated + */ + public function get_dom() + { + return $this->getDom(); + } + + /** + * @return DOMDocument + */ + public function getDom() + { + return $this->dom; + } + + /** + * @param Options $options + * @return $this + */ + public function setOptions(Options $options) + { + $this->options = $options; + $fontMetrics = $this->getFontMetrics(); + if (isset($fontMetrics)) { + $fontMetrics->setOptions($options); + } + return $this; + } + + /** + * @return Options + */ + public function getOptions() + { + return $this->options; + } + + /** + * @return array + * @deprecated + */ + public function get_callbacks() + { + return $this->getCallbacks(); + } + + /** + * Returns the callbacks array + * + * @return array + */ + public function getCallbacks() + { + return $this->callbacks; + } + + /** + * @param array $callbacks the set of callbacks to set + * @deprecated + */ + public function set_callbacks($callbacks) + { + $this->setCallbacks($callbacks); + } + + /** + * Sets callbacks for events like rendering of pages and elements. + * The callbacks array contains arrays with 'event' set to 'begin_page', + * 'end_page', 'begin_frame', or 'end_frame' and 'f' set to a function or + * object plus method to be called. + * + * The function 'f' must take an array as argument, which contains info + * about the event. + * + * @param array $callbacks the set of callbacks to set + */ + public function setCallbacks($callbacks) + { + if (is_array($callbacks)) { + $this->callbacks = array(); + foreach ($callbacks as $c) { + if (is_array($c) && isset($c['event']) && isset($c['f'])) { + $event = $c['event']; + $f = $c['f']; + if (is_callable($f) && is_string($event)) { + $this->callbacks[$event][] = $f; + } + } + } + } + } + + /** + * @return boolean + * @deprecated + */ + public function get_quirksmode() + { + return $this->getQuirksmode(); + } + + /** + * Get the quirks mode + * + * @return boolean true if quirks mode is active + */ + public function getQuirksmode() + { + return $this->quirksmode; + } + + /** + * @param FontMetrics $fontMetrics + * @return $this + */ + public function setFontMetrics(FontMetrics $fontMetrics) + { + $this->fontMetrics = $fontMetrics; + return $this; + } + + /** + * @return FontMetrics + */ + public function getFontMetrics() + { + return $this->fontMetrics; + } + + /** + * PHP5 overloaded getter + * Along with {@link Dompdf::__set()} __get() provides access to all + * properties directly. Typically __get() is not called directly outside + * of this class. + * + * @param string $prop + * + * @throws Exception + * @return mixed + */ + function __get($prop) + { + switch ($prop) + { + case 'version' : + return $this->version; + default: + throw new Exception( 'Invalid property: ' . $prop ); + } + } +} diff --git a/library/vendor/dompdf/src/Exception.php b/library/vendor/dompdf/src/Exception.php new file mode 100644 index 000000000..9729dc704 --- /dev/null +++ b/library/vendor/dompdf/src/Exception.php @@ -0,0 +1,30 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf; + +/** + * Standard exception thrown by DOMPDF classes + * + * @package dompdf + */ +class Exception extends \Exception +{ + + /** + * Class constructor + * + * @param string $message Error message + * @param int $code Error code + */ + function __construct($message = null, $code = 0) + { + parent::__construct($message, $code); + } + +} diff --git a/library/vendor/dompdf/src/Exception/ImageException.php b/library/vendor/dompdf/src/Exception/ImageException.php new file mode 100644 index 000000000..62b44b1c8 --- /dev/null +++ b/library/vendor/dompdf/src/Exception/ImageException.php @@ -0,0 +1,31 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Exception; + +use Dompdf\Exception; + +/** + * Image exception thrown by DOMPDF + * + * @package dompdf + */ +class ImageException extends Exception +{ + + /** + * Class constructor + * + * @param string $message Error message + * @param int $code Error code + */ + function __construct($message = null, $code = 0) + { + parent::__construct($message, $code); + } + +} diff --git a/library/vendor/dompdf/src/FontMetrics.php b/library/vendor/dompdf/src/FontMetrics.php new file mode 100644 index 000000000..d6601cbb2 --- /dev/null +++ b/library/vendor/dompdf/src/FontMetrics.php @@ -0,0 +1,603 @@ + + * @author Helmut Tischer + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf; + +use FontLib\Font; + +/** + * The font metrics class + * + * This class provides information about fonts and text. It can resolve + * font names into actual installed font files, as well as determine the + * size of text in a particular font and size. + * + * @static + * @package dompdf + */ +class FontMetrics +{ + /** + * Name of the font cache file + * + * This file must be writable by the webserver process only to update it + * with save_font_families() after adding the .afm file references of a new font family + * with FontMetrics::saveFontFamilies(). + * This is typically done only from command line with load_font.php on converting + * ttf fonts to ufm with php-font-lib. + */ + const CACHE_FILE = "dompdf_font_family_cache.php"; + + /** + * @var Canvas + * @deprecated + */ + protected $pdf; + + /** + * Underlying {@link Canvas} object to perform text size calculations + * + * @var Canvas + */ + protected $canvas; + + /** + * Array of font family names to font files + * + * Usually cached by the {@link load_font.php} script + * + * @var array + */ + protected $fontLookup = array(); + + /** + * @var Options + */ + private $options; + + /** + * Class initialization + */ + public function __construct(Canvas $canvas, Options $options) + { + $this->setCanvas($canvas); + $this->setOptions($options); + $this->loadFontFamilies(); + } + + /** + * @deprecated + */ + public function save_font_families() + { + $this->saveFontFamilies(); + } + + /** + * Saves the stored font family cache + * + * The name and location of the cache file are determined by {@link + * FontMetrics::CACHE_FILE}. This file should be writable by the + * webserver process. + * + * @see Font_Metrics::load_font_families() + */ + public function saveFontFamilies() + { + // replace the path to the DOMPDF font directories with the corresponding constants (allows for more portability) + $cacheData = sprintf("fontLookup as $family => $variants) { + $cacheData .= sprintf(" '%s' => array(%s", addslashes($family), PHP_EOL); + foreach ($variants as $variant => $path) { + $path = sprintf("'%s'", $path); + $path = str_replace('\'' . $this->getOptions()->getFontDir() , '$fontDir . \'' , $path); + $path = str_replace('\'' . $this->getOptions()->getRootDir() , '$rootDir . \'' , $path); + $cacheData .= sprintf(" '%s' => %s,%s", $variant, $path, PHP_EOL); + } + $cacheData .= sprintf(" ),%s", PHP_EOL); + } + $cacheData .= ") ?>"; + file_put_contents($this->getCacheFile(), $cacheData); + } + + /** + * @deprecated + */ + public function load_font_families() + { + $this->loadFontFamilies(); + } + + /** + * Loads the stored font family cache + * + * @see save_font_families() + */ + public function loadFontFamilies() + { + $fontDir = $this->getOptions()->getFontDir(); + $rootDir = $this->getOptions()->getRootDir(); + + // FIXME: tempoarary define constants for cache files <= v0.6.2 + if (!defined("DOMPDF_DIR")) { define("DOMPDF_DIR", $rootDir); } + if (!defined("DOMPDF_FONT_DIR")) { define("DOMPDF_FONT_DIR", $fontDir); } + + $file = $rootDir . "/lib/fonts/dompdf_font_family_cache.dist.php"; + $distFonts = require $file; + + // FIXME: temporary step for font cache created before the font cache fix + if (is_readable($fontDir . DIRECTORY_SEPARATOR . "dompdf_font_family_cache")) { + $oldFonts = require $fontDir . DIRECTORY_SEPARATOR . "dompdf_font_family_cache"; + // If the font family cache is still in the old format + if ($oldFonts === 1) { + $cacheData = file_get_contents($fontDir . DIRECTORY_SEPARATOR . "dompdf_font_family_cache"); + file_put_contents($fontDir . DIRECTORY_SEPARATOR . "dompdf_font_family_cache", "<" . "?php return $cacheData ?" . ">"); + $oldFonts = require $fontDir . DIRECTORY_SEPARATOR . "dompdf_font_family_cache"; + } + $distFonts += $oldFonts; + } + + if (!is_readable($this->getCacheFile())) { + $this->fontLookup = $distFonts; + return; + } + + $cacheData = require $this->getCacheFile(); + + // If the font family cache is still in the old format + if ($cacheData === 1) { + $cacheData = file_get_contents($this->getCacheFile()); + file_put_contents($this->getCacheFile(), "<" . "?php return $cacheData ?" . ">"); + $this->fontLookup = require $this->getCacheFile(); + } + + $this->fontLookup = array(); + foreach ($cacheData as $key => $value) { + $this->fontLookup[stripslashes($key)] = $value; + } + + // Merge provided fonts + $this->fontLookup += $distFonts; + } + + /** + * @param array $files + * @return array + * @deprecated + */ + public function install_fonts($files) + { + return $this->installFonts($files); + } + + /** + * @param array $files + * @return array + */ + public function installFonts(array $files) + { + $names = array(); + + foreach ($files as $file) { + $font = Font::load($file); + $records = $font->getData("name", "records"); + $type = $this->getType($records[2]); + $names[mb_strtolower($records[1])][$type] = $file; + $font->close(); + } + + return $names; + } + + /** + * @param array $style + * @param string $remote_file + * @param resource $context + * @return bool + * @deprecated + */ + public function register_font($style, $remote_file, $context = null) + { + return $this->registerFont($style, $remote_file); + } + + /** + * @param array $style + * @param string $remoteFile + * @param resource $context + * @return bool + */ + public function registerFont($style, $remoteFile, $context = null) + { + $fontDir = $this->getOptions()->getFontDir(); + $fontname = mb_strtolower($style["family"]); + $families = $this->getFontFamilies(); + + $entry = array(); + if (isset($families[$fontname])) { + $entry = $families[$fontname]; + } + + $localFile = $fontDir . DIRECTORY_SEPARATOR . md5($remoteFile); + $localTempFile = $this->options->get('tempDir') . "/" . md5($remoteFile); + $cacheEntry = $localFile; + $localFile .= ".ttf"; + + $styleString = $this->getType("{$style['weight']} {$style['style']}"); + + if ( !isset($entry[$styleString]) ) { + $entry[$styleString] = $cacheEntry; + + // Download the remote file + $remoteFileContent = @file_get_contents($remoteFile, null, $context); + if (false === $remoteFileContent) { + return false; + } + file_put_contents($localTempFile, $remoteFileContent); + + $font = Font::load($localTempFile); + + if (!$font) { + unlink($localTempFile); + return false; + } + + $font->parse(); + $font->saveAdobeFontMetrics("$cacheEntry.ufm"); + $font->close(); + + unlink($localTempFile); + + if ( !file_exists("$cacheEntry.ufm") ) { + return false; + } + + // Save the changes + file_put_contents($localFile, file_get_contents($remoteFile, null, $context)); + + if ( !file_exists($localFile) ) { + unlink("$cacheEntry.ufm"); + return false; + } + + $this->setFontFamily($fontname, $entry); + $this->saveFontFamilies(); + } + + return true; + } + + /** + * @param $text + * @param $font + * @param $size + * @param float $word_spacing + * @param float $char_spacing + * @return float + * @deprecated + */ + public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) + { + //return self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing); + return $this->getTextWidth($text, $font, $size, $word_spacing, $char_spacing); + } + + /** + * Calculates text size, in points + * + * @param string $text the text to be sized + * @param string $font the desired font + * @param float $size the desired font size + * @param float $wordSpacing + * @param float $charSpacing + * + * @internal param float $spacing word spacing, if any + * @return float + */ + public function getTextWidth($text, $font, $size, $wordSpacing = 0.0, $charSpacing = 0.0) + { + // @todo Make sure this cache is efficient before enabling it + static $cache = array(); + + if ($text === "") { + return 0; + } + + // Don't cache long strings + $useCache = !isset($text[50]); // Faster than strlen + + $key = "$font/$size/$wordSpacing/$charSpacing"; + + if ($useCache && isset($cache[$key][$text])) { + return $cache[$key]["$text"]; + } + + $width = $this->getCanvas()->get_text_width($text, $font, $size, $wordSpacing, $charSpacing); + + if ($useCache) { + $cache[$key][$text] = $width; + } + + return $width; + } + + /** + * @param $font + * @param $size + * @return float + * @deprecated + */ + public function get_font_height($font, $size) + { + return $this->getFontHeight($font, $size); + } + + /** + * Calculates font height + * + * @param string $font + * @param float $size + * + * @return float + */ + public function getFontHeight($font, $size) + { + return $this->getCanvas()->get_font_height($font, $size); + } + + /** + * @param $family_raw + * @param string $subtype_raw + * @return string + * @deprecated + */ + public function get_font($family_raw, $subtype_raw = "normal") + { + return $this->getFont($family_raw, $subtype_raw); + } + + /** + * Resolves a font family & subtype into an actual font file + * Subtype can be one of 'normal', 'bold', 'italic' or 'bold_italic'. If + * the particular font family has no suitable font file, the default font + * ({@link Options::defaultFont}) is used. The font file returned + * is the absolute pathname to the font file on the system. + * + * @param string $familyRaw + * @param string $subtypeRaw + * + * @return string + */ + public function getFont($familyRaw, $subtypeRaw = "normal") + { + static $cache = array(); + + if (isset($cache[$familyRaw][$subtypeRaw])) { + return $cache[$familyRaw][$subtypeRaw]; + } + + /* Allow calling for various fonts in search path. Therefore not immediately + * return replacement on non match. + * Only when called with NULL try replacement. + * When this is also missing there is really trouble. + * If only the subtype fails, nevertheless return failure. + * Only on checking the fallback font, check various subtypes on same font. + */ + + $subtype = strtolower($subtypeRaw); + + if ($familyRaw) { + $family = str_replace(array("'", '"'), "", strtolower($familyRaw)); + + if (isset($this->fontLookup[$family][$subtype])) { + return $cache[$familyRaw][$subtypeRaw] = $this->fontLookup[$family][$subtype]; + } + + return null; + } + + $family = "serif"; + + if (isset($this->fontLookup[$family][$subtype])) { + return $cache[$familyRaw][$subtypeRaw] = $this->fontLookup[$family][$subtype]; + } + + if (!isset($this->fontLookup[$family])) { + return null; + } + + $family = $this->fontLookup[$family]; + + foreach ($family as $sub => $font) { + if (strpos($subtype, $sub) !== false) { + return $cache[$familyRaw][$subtypeRaw] = $font; + } + } + + if ($subtype !== "normal") { + foreach ($family as $sub => $font) { + if ($sub !== "normal") { + return $cache[$familyRaw][$subtypeRaw] = $font; + } + } + } + + $subtype = "normal"; + + if (isset($family[$subtype])) { + return $cache[$familyRaw][$subtypeRaw] = $family[$subtype]; + } + + return null; + } + + /** + * @param $family + * @return null|string + * @deprecated + */ + public function get_family($family) + { + return $this->getFamily($family); + } + + /** + * @param string $family + * @return null|string + */ + public function getFamily($family) + { + $family = str_replace(array("'", '"'), "", mb_strtolower($family)); + + if (isset($this->fontLookup[$family])) { + return $this->fontLookup[$family]; + } + + return null; + } + + /** + * @param $type + * @return string + * @deprecated + */ + public function get_type($type) + { + return $this->getType($type); + } + + /** + * @param string $type + * @return string + */ + public function getType($type) + { + if (preg_match("/bold/i", $type)) { + if (preg_match("/italic|oblique/i", $type)) { + $type = "bold_italic"; + } else { + $type = "bold"; + } + } elseif (preg_match("/italic|oblique/i", $type)) { + $type = "italic"; + } else { + $type = "normal"; + } + + return $type; + } + + /** + * @return array + * @deprecated + */ + public function get_system_fonts() + { + return $this->getSystemFonts(); + } + + /** + * @return array + */ + public function getSystemFonts() + { + $files = glob("/usr/share/fonts/truetype/*.ttf") + + glob("/usr/share/fonts/truetype/*/*.ttf") + + glob("/usr/share/fonts/truetype/*/*/*.ttf") + + glob("C:\\Windows\\fonts\\*.ttf") + + glob("C:\\WinNT\\fonts\\*.ttf") + + glob("/mnt/c_drive/WINDOWS/Fonts/"); + + return $this->installFonts($files); + } + + /** + * @return array + * @deprecated + */ + public function get_font_families() + { + return $this->getFontFamilies(); + } + + /** + * Returns the current font lookup table + * + * @return array + */ + public function getFontFamilies() + { + return $this->fontLookup; + } + + /** + * @param string $fontname + * @param mixed $entry + * @deprecated + */ + public function set_font_family($fontname, $entry) + { + $this->setFontFamily($fontname, $entry); + } + + /** + * @param string $fontname + * @param mixed $entry + */ + public function setFontFamily($fontname, $entry) + { + $this->fontLookup[mb_strtolower($fontname)] = $entry; + } + + /** + * @return string + */ + public function getCacheFile() + { + return $this->getOptions()->getFontDir() . DIRECTORY_SEPARATOR . self::CACHE_FILE; + } + + /** + * @param Options $options + * @return $this + */ + public function setOptions(Options $options) + { + $this->options = $options; + return $this; + } + + /** + * @return Options + */ + public function getOptions() + { + return $this->options; + } + + /** + * @param Canvas $canvas + * @return $this + */ + public function setCanvas(Canvas $canvas) + { + $this->pdf = $canvas; + $this->canvas = $canvas; + return $this; + } + + /** + * @return Canvas + */ + public function getCanvas() + { + return $this->canvas; + } +} \ No newline at end of file diff --git a/library/vendor/dompdf/src/Frame.php b/library/vendor/dompdf/src/Frame.php new file mode 100644 index 000000000..6c2491d04 --- /dev/null +++ b/library/vendor/dompdf/src/Frame.php @@ -0,0 +1,1109 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +/** + * The main Frame class + * + * This class represents a single HTML element. This class stores + * positioning information as well as containing block location and + * dimensions. Style information for the element is stored in a {@link + * Style} object. Tree structure is maintained via the parent & children + * links. + * + * @package dompdf + */ +class Frame +{ + const WS_TEXT = 1; + const WS_SPACE = 2; + + /** + * The DOMElement or DOMText object this frame represents + * + * @var \DOMElement|\DOMText + */ + protected $_node; + + /** + * Unique identifier for this frame. Used to reference this frame + * via the node. + * + * @var string + */ + protected $_id; + + /** + * Unique id counter + */ + public static $ID_COUNTER = 0; /*protected*/ + + /** + * This frame's calculated style + * + * @var Style + */ + protected $_style; + + /** + * This frame's original style. Needed for cases where frames are + * split across pages. + * + * @var Style + */ + protected $_original_style; + + /** + * This frame's parent in the document tree. + * + * @var Frame + */ + protected $_parent; + + /** + * This frame's children + * + * @var Frame[] + */ + protected $_frame_list; + + /** + * This frame's first child. All children are handled as a + * doubly-linked list. + * + * @var Frame + */ + protected $_first_child; + + /** + * This frame's last child. + * + * @var Frame + */ + protected $_last_child; + + /** + * This frame's previous sibling in the document tree. + * + * @var Frame + */ + protected $_prev_sibling; + + /** + * This frame's next sibling in the document tree. + * + * @var Frame + */ + protected $_next_sibling; + + /** + * This frame's containing block (used in layout): array(x, y, w, h) + * + * @var float[] + */ + protected $_containing_block; + + /** + * Position on the page of the top-left corner of the margin box of + * this frame: array(x,y) + * + * @var float[] + */ + protected $_position; + + /** + * Absolute opacity of this frame + * + * @var float + */ + protected $_opacity; + + /** + * This frame's decorator + * + * @var \Dompdf\FrameDecorator\AbstractFrameDecorator + */ + protected $_decorator; + + /** + * This frame's containing line box + * + * @var LineBox + */ + protected $_containing_line; + + /** + * @var array + */ + protected $_is_cache = array(); + + /** + * Tells wether the frame was already pushed to the next page + * + * @var bool + */ + public $_already_pushed = false; + + /** + * @var bool + */ + public $_float_next_line = false; + + /** + * Tells wether the frame was split + * + * @var bool + */ + public $_splitted; + + /** + * @var int + */ + public static $_ws_state = self::WS_SPACE; + + /** + * Class constructor + * + * @param \DOMNode $node the DOMNode this frame represents + */ + public function __construct(\DOMNode $node) + { + $this->_node = $node; + + $this->_parent = null; + $this->_first_child = null; + $this->_last_child = null; + $this->_prev_sibling = $this->_next_sibling = null; + + $this->_style = null; + $this->_original_style = null; + + $this->_containing_block = array( + "x" => null, + "y" => null, + "w" => null, + "h" => null, + ); + + $this->_containing_block[0] =& $this->_containing_block["x"]; + $this->_containing_block[1] =& $this->_containing_block["y"]; + $this->_containing_block[2] =& $this->_containing_block["w"]; + $this->_containing_block[3] =& $this->_containing_block["h"]; + + $this->_position = array( + "x" => null, + "y" => null, + ); + + $this->_position[0] =& $this->_position["x"]; + $this->_position[1] =& $this->_position["y"]; + + $this->_opacity = 1.0; + $this->_decorator = null; + + $this->set_id(self::$ID_COUNTER++); + } + + /** + * WIP : preprocessing to remove all the unused whitespace + */ + protected function ws_trim() + { + if ($this->ws_keep()) { + return; + } + + if (self::$_ws_state === self::WS_SPACE) { + $node = $this->_node; + + if ($node->nodeName === "#text" && !empty($node->nodeValue)) { + $node->nodeValue = preg_replace("/[ \t\r\n\f]+/u", " ", trim($node->nodeValue)); + self::$_ws_state = self::WS_TEXT; + } + } + } + + /** + * @return bool + */ + protected function ws_keep() + { + $whitespace = $this->get_style()->white_space; + + return in_array($whitespace, array("pre", "pre-wrap", "pre-line")); + } + + /** + * @return bool + */ + protected function ws_is_text() + { + $node = $this->get_node(); + + if ($node->nodeName === "img") { + return true; + } + + if (!$this->is_in_flow()) { + return false; + } + + if ($this->is_text_node()) { + return trim($node->nodeValue) !== ""; + } + + return true; + } + + /** + * "Destructor": forcibly free all references held by this frame + * + * @param bool $recursive if true, call dispose on all children + */ + public function dispose($recursive = false) + { + if ($recursive) { + while ($child = $this->_first_child) { + $child->dispose(true); + } + } + + // Remove this frame from the tree + if ($this->_prev_sibling) { + $this->_prev_sibling->_next_sibling = $this->_next_sibling; + } + + if ($this->_next_sibling) { + $this->_next_sibling->_prev_sibling = $this->_prev_sibling; + } + + if ($this->_parent && $this->_parent->_first_child === $this) { + $this->_parent->_first_child = $this->_next_sibling; + } + + if ($this->_parent && $this->_parent->_last_child === $this) { + $this->_parent->_last_child = $this->_prev_sibling; + } + + if ($this->_parent) { + $this->_parent->get_node()->removeChild($this->_node); + } + + $this->_style->dispose(); + $this->_style = null; + unset($this->_style); + + $this->_original_style->dispose(); + $this->_original_style = null; + unset($this->_original_style); + + } + + /** + * Re-initialize the frame + */ + public function reset() + { + $this->_position["x"] = null; + $this->_position["y"] = null; + + $this->_containing_block["x"] = null; + $this->_containing_block["y"] = null; + $this->_containing_block["w"] = null; + $this->_containing_block["h"] = null; + + $this->_style = null; + unset($this->_style); + $this->_style = clone $this->_original_style; + } + + /** + * @return \DOMElement|\DOMText + */ + public function get_node() + { + return $this->_node; + } + + /** + * @return string + */ + public function get_id() + { + return $this->_id; + } + + /** + * @return Style + */ + public function get_style() + { + return $this->_style; + } + + /** + * @return Style + */ + public function get_original_style() + { + return $this->_original_style; + } + + /** + * @return Frame + */ + public function get_parent() + { + return $this->_parent; + } + + /** + * @return \Dompdf\FrameDecorator\AbstractFrameDecorator + */ + public function get_decorator() + { + return $this->_decorator; + } + + /** + * @return Frame + */ + public function get_first_child() + { + return $this->_first_child; + } + + /** + * @return Frame + */ + public function get_last_child() + { + return $this->_last_child; + } + + /** + * @return Frame + */ + public function get_prev_sibling() + { + return $this->_prev_sibling; + } + + /** + * @return Frame + */ + public function get_next_sibling() + { + return $this->_next_sibling; + } + + /** + * @return FrameList|Frame[] + */ + public function get_children() + { + if (isset($this->_frame_list)) { + return $this->_frame_list; + } + + $this->_frame_list = new FrameList($this); + + return $this->_frame_list; + } + + // Layout property accessors + + /** + * Containing block dimensions + * + * @param $i string The key of the wanted containing block's dimension (x, y, x, h) + * + * @return float[]|float + */ + public function get_containing_block($i = null) + { + if (isset($i)) { + return $this->_containing_block[$i]; + } + + return $this->_containing_block; + } + + /** + * Block position + * + * @param $i string The key of the wanted position value (x, y) + * + * @return array|float + */ + public function get_position($i = null) + { + if (isset($i)) { + return $this->_position[$i]; + } + + return $this->_position; + } + + //........................................................................ + + /** + * Return the height of the margin box of the frame, in pt. Meaningless + * unless the height has been calculated properly. + * + * @return float + */ + public function get_margin_height() + { + $style = $this->_style; + + return $style->length_in_pt(array( + $style->height, + $style->margin_top, + $style->margin_bottom, + $style->border_top_width, + $style->border_bottom_width, + $style->padding_top, + $style->padding_bottom + ), $this->_containing_block["h"]); + } + + /** + * Return the width of the margin box of the frame, in pt. Meaningless + * unless the width has been calculated properly. + * + * @return float + */ + public function get_margin_width() + { + $style = $this->_style; + + return $style->length_in_pt(array( + $style->width, + $style->margin_left, + $style->margin_right, + $style->border_left_width, + $style->border_right_width, + $style->padding_left, + $style->padding_right + ), $this->_containing_block["w"]); + } + + /** + * @return float + */ + public function get_break_margins() + { + $style = $this->_style; + + return $style->length_in_pt(array( + //$style->height, + $style->margin_top, + $style->margin_bottom, + $style->border_top_width, + $style->border_bottom_width, + $style->padding_top, + $style->padding_bottom + ), $this->_containing_block["h"]); + } + + /** + * Return the padding box (x,y,w,h) of the frame + * + * @return array + */ + public function get_padding_box() + { + $style = $this->_style; + $cb = $this->_containing_block; + + $x = $this->_position["x"] + + $style->length_in_pt(array($style->margin_left, + $style->border_left_width), + $cb["w"]); + + $y = $this->_position["y"] + + $style->length_in_pt(array($style->margin_top, + $style->border_top_width), + $cb["h"]); + + $w = $style->length_in_pt(array($style->padding_left, + $style->width, + $style->padding_right), + $cb["w"]); + + $h = $style->length_in_pt(array($style->padding_top, + $style->height, + $style->padding_bottom), + $cb["h"]); + + return array(0 => $x, "x" => $x, + 1 => $y, "y" => $y, + 2 => $w, "w" => $w, + 3 => $h, "h" => $h); + } + + /** + * Return the border box of the frame + * + * @return array + */ + public function get_border_box() + { + $style = $this->_style; + $cb = $this->_containing_block; + + $x = $this->_position["x"] + $style->length_in_pt($style->margin_left, $cb["w"]); + + $y = $this->_position["y"] + $style->length_in_pt($style->margin_top, $cb["h"]); + + $w = $style->length_in_pt(array($style->border_left_width, + $style->padding_left, + $style->width, + $style->padding_right, + $style->border_right_width), + $cb["w"]); + + $h = $style->length_in_pt(array($style->border_top_width, + $style->padding_top, + $style->height, + $style->padding_bottom, + $style->border_bottom_width), + $cb["h"]); + + return array(0 => $x, "x" => $x, + 1 => $y, "y" => $y, + 2 => $w, "w" => $w, + 3 => $h, "h" => $h); + } + + /** + * @param null $opacity + * + * @return float + */ + public function get_opacity($opacity = null) + { + if ($opacity !== null) { + $this->set_opacity($opacity); + } + + return $this->_opacity; + } + + /** + * @return LineBox + */ + public function &get_containing_line() + { + return $this->_containing_line; + } + + //........................................................................ + + // Set methods + /** + * @param $id + */ + public function set_id($id) + { + $this->_id = $id; + + // We can only set attributes of DOMElement objects (nodeType == 1). + // Since these are the only objects that we can assign CSS rules to, + // this shortcoming is okay. + if ($this->_node->nodeType == XML_ELEMENT_NODE) { + $this->_node->setAttribute("frame_id", $id); + } + } + + /** + * @param Style $style + */ + public function set_style(Style $style) + { + if (is_null($this->_style)) { + $this->_original_style = clone $style; + } + + //$style->set_frame($this); + $this->_style = $style; + } + + /** + * @param \Dompdf\FrameDecorator\AbstractFrameDecorator $decorator + */ + public function set_decorator(FrameDecorator\AbstractFrameDecorator $decorator) + { + $this->_decorator = $decorator; + } + + /** + * @param null $x + * @param null $y + * @param null $w + * @param null $h + */ + public function set_containing_block($x = null, $y = null, $w = null, $h = null) + { + if (is_array($x)) { + foreach ($x as $key => $val) { + $$key = $val; + } + } + + if (is_numeric($x)) { + $this->_containing_block["x"] = $x; + } + + if (is_numeric($y)) { + $this->_containing_block["y"] = $y; + } + + if (is_numeric($w)) { + $this->_containing_block["w"] = $w; + } + + if (is_numeric($h)) { + $this->_containing_block["h"] = $h; + } + } + + /** + * @param null $x + * @param null $y + */ + public function set_position($x = null, $y = null) + { + if (is_array($x)) { + list($x, $y) = array($x["x"], $x["y"]); + } + + if (is_numeric($x)) { + $this->_position["x"] = $x; + } + + if (is_numeric($y)) { + $this->_position["y"] = $y; + } + } + + /** + * @param $opacity + */ + public function set_opacity($opacity) + { + $parent = $this->get_parent(); + $base_opacity = (($parent && $parent->_opacity !== null) ? $parent->_opacity : 1.0); + $this->_opacity = $base_opacity * $opacity; + } + + /** + * @param LineBox $line + */ + public function set_containing_line(LineBox $line) + { + $this->_containing_line = $line; + } + + /** + * Tells if the frame is a text node + * + * @return bool + */ + public function is_text_node() + { + if (isset($this->_is_cache["text_node"])) { + return $this->_is_cache["text_node"]; + } + + return $this->_is_cache["text_node"] = ($this->get_node()->nodeName === "#text"); + } + + /** + * @return bool + */ + public function is_positionned() + { + if (isset($this->_is_cache["positionned"])) { + return $this->_is_cache["positionned"]; + } + + $position = $this->get_style()->position; + + return $this->_is_cache["positionned"] = in_array($position, Style::$POSITIONNED_TYPES); + } + + /** + * @return bool + */ + public function is_absolute() + { + if (isset($this->_is_cache["absolute"])) { + return $this->_is_cache["absolute"]; + } + + $position = $this->get_style()->position; + + return $this->_is_cache["absolute"] = ($position === "absolute" || $position === "fixed"); + } + + /** + * @return bool + */ + public function is_block() + { + if (isset($this->_is_cache["block"])) { + return $this->_is_cache["block"]; + } + + return $this->_is_cache["block"] = in_array($this->get_style()->display, Style::$BLOCK_TYPES); + } + + /** + * @return bool + */ + public function is_in_flow() + { + if (isset($this->_is_cache["in_flow"])) { + return $this->_is_cache["in_flow"]; + } + return $this->_is_cache["in_flow"] = !($this->get_style()->float !== "none" || $this->is_absolute()); + } + + /** + * @return bool + */ + public function is_pre() + { + if (isset($this->_is_cache["pre"])) { + return $this->_is_cache["pre"]; + } + + $white_space = $this->get_style()->white_space; + + return $this->_is_cache["pre"] = in_array($white_space, array("pre", "pre-wrap")); + } + + /** + * @return bool + */ + public function is_table() + { + if (isset($this->_is_cache["table"])) { + return $this->_is_cache["table"]; + } + + $display = $this->get_style()->display; + + return $this->_is_cache["table"] = in_array($display, Style::$TABLE_TYPES); + } + + + /** + * Inserts a new child at the beginning of the Frame + * + * @param $child Frame The new Frame to insert + * @param $update_node boolean Whether or not to update the DOM + */ + public function prepend_child(Frame $child, $update_node = true) + { + if ($update_node) { + $this->_node->insertBefore($child->_node, $this->_first_child ? $this->_first_child->_node : null); + } + + // Remove the child from its parent + if ($child->_parent) { + $child->_parent->remove_child($child, false); + } + + $child->_parent = $this; + $child->_prev_sibling = null; + + // Handle the first child + if (!$this->_first_child) { + $this->_first_child = $child; + $this->_last_child = $child; + $child->_next_sibling = null; + } else { + $this->_first_child->_prev_sibling = $child; + $child->_next_sibling = $this->_first_child; + $this->_first_child = $child; + } + } + + /** + * Inserts a new child at the end of the Frame + * + * @param $child Frame The new Frame to insert + * @param $update_node boolean Whether or not to update the DOM + */ + public function append_child(Frame $child, $update_node = true) + { + if ($update_node) { + $this->_node->appendChild($child->_node); + } + + // Remove the child from its parent + if ($child->_parent) { + $child->_parent->remove_child($child, false); + } + + $child->_parent = $this; + $child->_next_sibling = null; + + // Handle the first child + if (!$this->_last_child) { + $this->_first_child = $child; + $this->_last_child = $child; + $child->_prev_sibling = null; + } else { + $this->_last_child->_next_sibling = $child; + $child->_prev_sibling = $this->_last_child; + $this->_last_child = $child; + } + } + + /** + * Inserts a new child immediately before the specified frame + * + * @param $new_child Frame The new Frame to insert + * @param $ref Frame The Frame after the new Frame + * @param $update_node boolean Whether or not to update the DOM + * + * @throws Exception + */ + public function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) + { + if ($ref === $this->_first_child) { + $this->prepend_child($new_child, $update_node); + + return; + } + + if (is_null($ref)) { + $this->append_child($new_child, $update_node); + + return; + } + + if ($ref->_parent !== $this) { + throw new Exception("Reference child is not a child of this node."); + } + + // Update the node + if ($update_node) { + $this->_node->insertBefore($new_child->_node, $ref->_node); + } + + // Remove the child from its parent + if ($new_child->_parent) { + $new_child->_parent->remove_child($new_child, false); + } + + $new_child->_parent = $this; + $new_child->_next_sibling = $ref; + $new_child->_prev_sibling = $ref->_prev_sibling; + + if ($ref->_prev_sibling) { + $ref->_prev_sibling->_next_sibling = $new_child; + } + + $ref->_prev_sibling = $new_child; + } + + /** + * Inserts a new child immediately after the specified frame + * + * @param $new_child Frame The new Frame to insert + * @param $ref Frame The Frame before the new Frame + * @param $update_node boolean Whether or not to update the DOM + * + * @throws Exception + */ + public function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) + { + if ($ref === $this->_last_child) { + $this->append_child($new_child, $update_node); + + return; + } + + if (is_null($ref)) { + $this->prepend_child($new_child, $update_node); + + return; + } + + if ($ref->_parent !== $this) { + throw new Exception("Reference child is not a child of this node."); + } + + // Update the node + if ($update_node) { + if ($ref->_next_sibling) { + $next_node = $ref->_next_sibling->_node; + $this->_node->insertBefore($new_child->_node, $next_node); + } else { + $new_child->_node = $this->_node->appendChild($new_child->_node); + } + } + + // Remove the child from its parent + if ($new_child->_parent) { + $new_child->_parent->remove_child($new_child, false); + } + + $new_child->_parent = $this; + $new_child->_prev_sibling = $ref; + $new_child->_next_sibling = $ref->_next_sibling; + + if ($ref->_next_sibling) { + $ref->_next_sibling->_prev_sibling = $new_child; + } + + $ref->_next_sibling = $new_child; + } + + /** + * Remove a child frame + * + * @param Frame $child + * @param boolean $update_node Whether or not to remove the DOM node + * + * @throws Exception + * @return Frame The removed child frame + */ + public function remove_child(Frame $child, $update_node = true) + { + if ($child->_parent !== $this) { + throw new Exception("Child not found in this frame"); + } + + if ($update_node) { + $this->_node->removeChild($child->_node); + } + + if ($child === $this->_first_child) { + $this->_first_child = $child->_next_sibling; + } + + if ($child === $this->_last_child) { + $this->_last_child = $child->_prev_sibling; + } + + if ($child->_prev_sibling) { + $child->_prev_sibling->_next_sibling = $child->_next_sibling; + } + + if ($child->_next_sibling) { + $child->_next_sibling->_prev_sibling = $child->_prev_sibling; + } + + $child->_next_sibling = null; + $child->_prev_sibling = null; + $child->_parent = null; + + return $child; + } + + //........................................................................ + + // Debugging function: + /** + * @return string + */ + public function __toString() + { + // Skip empty text frames +// if ( $this->is_text_node() && +// preg_replace("/\s/", "", $this->_node->data) === "" ) +// return ""; + + + $str = "" . $this->_node->nodeName . ":
"; + //$str .= spl_object_hash($this->_node) . "
"; + $str .= "Id: " . $this->get_id() . "
"; + $str .= "Class: " . get_class($this) . "
"; + + if ($this->is_text_node()) { + $tmp = htmlspecialchars($this->_node->nodeValue); + $str .= "
'" . mb_substr($tmp, 0, 70) .
+                (mb_strlen($tmp) > 70 ? "..." : "") . "'
"; + } elseif ($css_class = $this->_node->getAttribute("class")) { + $str .= "CSS class: '$css_class'
"; + } + + if ($this->_parent) { + $str .= "\nParent:" . $this->_parent->_node->nodeName . + " (" . spl_object_hash($this->_parent->_node) . ") " . + "
"; + } + + if ($this->_prev_sibling) { + $str .= "Prev: " . $this->_prev_sibling->_node->nodeName . + " (" . spl_object_hash($this->_prev_sibling->_node) . ") " . + "
"; + } + + if ($this->_next_sibling) { + $str .= "Next: " . $this->_next_sibling->_node->nodeName . + " (" . spl_object_hash($this->_next_sibling->_node) . ") " . + "
"; + } + + $d = $this->get_decorator(); + while ($d && $d != $d->get_decorator()) { + $str .= "Decorator: " . get_class($d) . "
"; + $d = $d->get_decorator(); + } + + $str .= "Position: " . Helpers::pre_r($this->_position, true); + $str .= "\nContaining block: " . Helpers::pre_r($this->_containing_block, true); + $str .= "\nMargin width: " . Helpers::pre_r($this->get_margin_width(), true); + $str .= "\nMargin height: " . Helpers::pre_r($this->get_margin_height(), true); + + $str .= "\nStyle:
" . $this->_style->__toString() . "
"; + + if ($this->_decorator instanceof FrameDecorator\Block) { + $str .= "Lines:
";
+            foreach ($this->_decorator->get_line_boxes() as $line) {
+                foreach ($line->get_frames() as $frame) {
+                    if ($frame instanceof FrameDecorator\Text) {
+                        $str .= "\ntext: ";
+                        $str .= "'" . htmlspecialchars($frame->get_text()) . "'";
+                    } else {
+                        $str .= "\nBlock: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")";
+                    }
+                }
+
+                $str .=
+                    "\ny => " . $line->y . "\n" .
+                    "w => " . $line->w . "\n" .
+                    "h => " . $line->h . "\n" .
+                    "left => " . $line->left . "\n" .
+                    "right => " . $line->right . "\n";
+            }
+            $str .= "
"; + } + + $str .= "\n"; + if (php_sapi_name() === "cli") { + $str = strip_tags(str_replace(array("
", "", ""), + array("\n", "", ""), + $str)); + } + + return $str; + } +} \ No newline at end of file diff --git a/library/vendor/dompdf/src/Frame/Factory.php b/library/vendor/dompdf/src/Frame/Factory.php new file mode 100644 index 000000000..431a5c10c --- /dev/null +++ b/library/vendor/dompdf/src/Frame/Factory.php @@ -0,0 +1,262 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Frame; + +use Dompdf\Css\Style; +use Dompdf\Dompdf; +use Dompdf\Exception; +use Dompdf\Frame; +use Dompdf\FrameDecorator\AbstractFrameDecorator; +use DOMXPath; +use Dompdf\FrameDecorator\Page as PageFrameDecorator; +use Dompdf\FrameReflower\Page as PageFrameReflower; + +/** + * Contains frame decorating logic + * + * This class is responsible for assigning the correct {@link AbstractFrameDecorator}, + * {@link AbstractPositioner}, and {@link AbstractFrameReflower} objects to {@link Frame} + * objects. This is determined primarily by the Frame's display type, but + * also by the Frame's node's type (e.g. DomElement vs. #text) + * + * @access private + * @package dompdf + */ +class Factory +{ + + /** + * Decorate the root Frame + * + * @param $root Frame The frame to decorate + * @param $dompdf Dompdf The dompdf instance + * + * @return PageFrameDecorator + */ + static function decorate_root(Frame $root, Dompdf $dompdf) + { + $frame = new PageFrameDecorator($root, $dompdf); + $frame->set_reflower(new PageFrameReflower($frame)); + $root->set_decorator($frame); + + return $frame; + } + + /** + * Decorate a Frame + * + * @param Frame $frame The frame to decorate + * @param Dompdf $dompdf The dompdf instance + * @param Frame $root The frame to decorate + * + * @throws Exception + * @return AbstractFrameDecorator + * FIXME: this is admittedly a little smelly... + */ + static function decorate_frame(Frame $frame, Dompdf $dompdf, Frame $root = null) + { + if (is_null($dompdf)) { + throw new Exception("The DOMPDF argument is required"); + } + + $style = $frame->get_style(); + + // Floating (and more generally out-of-flow) elements are blocks + // http://coding.smashingmagazine.com/2007/05/01/css-float-theory-things-you-should-know/ + if (!$frame->is_in_flow() && in_array($style->display, Style::$INLINE_TYPES)) { + $style->display = "block"; + } + + $display = $style->display; + + switch ($display) { + + case "block": + $positioner = "Block"; + $decorator = "Block"; + $reflower = "Block"; + break; + + case "inline-block": + $positioner = "Inline"; + $decorator = "Block"; + $reflower = "Block"; + break; + + case "inline": + $positioner = "Inline"; + if ($frame->is_text_node()) { + $decorator = "Text"; + $reflower = "Text"; + } else { + if ($style->float !== "none") { + $decorator = "Block"; + $reflower = "Block"; + } else { + $decorator = "Inline"; + $reflower = "Inline"; + } + } + break; + + case "table": + $positioner = "Block"; + $decorator = "Table"; + $reflower = "Table"; + break; + + case "inline-table": + $positioner = "Inline"; + $decorator = "Table"; + $reflower = "Table"; + break; + + case "table-row-group": + case "table-header-group": + case "table-footer-group": + $positioner = "NullPositioner"; + $decorator = "TableRowGroup"; + $reflower = "TableRowGroup"; + break; + + case "table-row": + $positioner = "NullPositioner"; + $decorator = "TableRow"; + $reflower = "TableRow"; + break; + + case "table-cell": + $positioner = "TableCell"; + $decorator = "TableCell"; + $reflower = "TableCell"; + break; + + case "list-item": + $positioner = "Block"; + $decorator = "Block"; + $reflower = "Block"; + break; + + case "-dompdf-list-bullet": + if ($style->list_style_position === "inside") { + $positioner = "Inline"; + } else { + $positioner = "ListBullet"; + } + + if ($style->list_style_image !== "none") { + $decorator = "ListBulletImage"; + } else { + $decorator = "ListBullet"; + } + + $reflower = "ListBullet"; + break; + + case "-dompdf-image": + $positioner = "Inline"; + $decorator = "Image"; + $reflower = "Image"; + break; + + case "-dompdf-br": + $positioner = "Inline"; + $decorator = "Inline"; + $reflower = "Inline"; + break; + + default: + // FIXME: should throw some sort of warning or something? + case "none": + if ($style->_dompdf_keep !== "yes") { + // Remove the node and the frame + $frame->get_parent()->remove_child($frame); + return; + } + + $positioner = "NullPositioner"; + $decorator = "NullFrameDecorator"; + $reflower = "NullFrameReflower"; + break; + } + + // Handle CSS position + $position = $style->position; + + if ($position === "absolute") { + $positioner = "Absolute"; + } else { + if ($position === "fixed") { + $positioner = "Fixed"; + } + } + + $node = $frame->get_node(); + + // Handle nodeName + if ($node->nodeName === "img") { + $style->display = "-dompdf-image"; + $decorator = "Image"; + $reflower = "Image"; + } + + $positioner = "Dompdf\\Positioner\\$positioner"; + $decorator = "Dompdf\\FrameDecorator\\$decorator"; + $reflower = "Dompdf\\FrameReflower\\$reflower"; + + /** @var AbstractFrameDecorator $deco */ + $deco = new $decorator($frame, $dompdf); + + $deco->set_positioner(new $positioner($deco)); + $deco->set_reflower(new $reflower($deco, $dompdf->getFontMetrics())); + + if ($root) { + $deco->set_root($root); + } + + if ($display === "list-item") { + // Insert a list-bullet frame + $xml = $dompdf->get_dom(); + $bullet_node = $xml->createElement("bullet"); // arbitrary choice + $b_f = new Frame($bullet_node); + + $node = $frame->get_node(); + $parent_node = $node->parentNode; + + if ($parent_node) { + if (!$parent_node->hasAttribute("dompdf-children-count")) { + $xpath = new DOMXPath($xml); + $count = $xpath->query("li", $parent_node)->length; + $parent_node->setAttribute("dompdf-children-count", $count); + } + + if (is_numeric($node->getAttribute("value"))) { + $index = intval($node->getAttribute("value")); + } else { + if (!$parent_node->hasAttribute("dompdf-counter")) { + $index = ($parent_node->hasAttribute("start") ? $parent_node->getAttribute("start") : 1); + } else { + $index = $parent_node->getAttribute("dompdf-counter") + 1; + } + } + + $parent_node->setAttribute("dompdf-counter", $index); + $bullet_node->setAttribute("dompdf-counter", $index); + } + + $new_style = $dompdf->get_css()->create_style(); + $new_style->display = "-dompdf-list-bullet"; + $new_style->inherit($style); + $b_f->set_style($new_style); + + $deco->prepend_child(Factory::decorate_frame($b_f, $dompdf, $root)); + } + + return $deco; + } +} diff --git a/library/vendor/dompdf/src/Frame/FrameList.php b/library/vendor/dompdf/src/Frame/FrameList.php new file mode 100644 index 000000000..783600dc7 --- /dev/null +++ b/library/vendor/dompdf/src/Frame/FrameList.php @@ -0,0 +1,34 @@ +_frame = $frame; + } + + /** + * @return FrameListIterator + */ + function getIterator() + { + return new FrameListIterator($this->_frame); + } +} diff --git a/library/vendor/dompdf/src/Frame/FrameListIterator.php b/library/vendor/dompdf/src/Frame/FrameListIterator.php new file mode 100644 index 000000000..ada9dde18 --- /dev/null +++ b/library/vendor/dompdf/src/Frame/FrameListIterator.php @@ -0,0 +1,91 @@ +_parent = $frame; + $this->_cur = $frame->get_first_child(); + $this->_num = 0; + } + + /** + * + */ + public function rewind() + { + $this->_cur = $this->_parent->get_first_child(); + $this->_num = 0; + } + + /** + * @return bool + */ + public function valid() + { + return isset($this->_cur); // && ($this->_cur->get_prev_sibling() === $this->_prev); + } + + /** + * @return int + */ + public function key() + { + return $this->_num; + } + + /** + * @return Frame + */ + public function current() + { + return $this->_cur; + } + + /** + * @return Frame + */ + public function next() + { + $ret = $this->_cur; + if (!$ret) { + return null; + } + + $this->_cur = $this->_cur->get_next_sibling(); + $this->_num++; + return $ret; + } +} \ No newline at end of file diff --git a/library/vendor/dompdf/src/Frame/FrameTree.php b/library/vendor/dompdf/src/Frame/FrameTree.php new file mode 100644 index 000000000..ff882ff33 --- /dev/null +++ b/library/vendor/dompdf/src/Frame/FrameTree.php @@ -0,0 +1,300 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +/** + * Represents an entire document as a tree of frames + * + * The FrameTree consists of {@link Frame} objects each tied to specific + * DOMNode objects in a specific DomDocument. The FrameTree has the same + * structure as the DomDocument, but adds additional capabalities for + * styling and layout. + * + * @package dompdf + */ +class FrameTree +{ + /** + * Tags to ignore while parsing the tree + * + * @var array + */ + protected static $HIDDEN_TAGS = array( + "area", + "base", + "basefont", + "head", + "style", + "meta", + "title", + "colgroup", + "noembed", + "noscript", + "param", + "#comment" + ); + + /** + * The main DomDocument + * + * @see http://ca2.php.net/manual/en/ref.dom.php + * @var DOMDocument + */ + protected $_dom; + + /** + * The root node of the FrameTree. + * + * @var Frame + */ + protected $_root; + + /** + * Subtrees of absolutely positioned elements + * + * @var array of Frames + */ + protected $_absolute_frames; + + /** + * A mapping of {@link Frame} objects to DOMNode objects + * + * @var array + */ + protected $_registry; + + /** + * Class constructor + * + * @param DOMDocument $dom the main DomDocument object representing the current html document + */ + public function __construct(DomDocument $dom) + { + $this->_dom = $dom; + $this->_root = null; + $this->_registry = array(); + } + + /** + * Returns the DOMDocument object representing the curent html document + * + * @return DOMDocument + */ + public function get_dom() + { + return $this->_dom; + } + + /** + * Returns the root frame of the tree + * + * @return \Dompdf\FrameDecorator\Page + */ + public function get_root() + { + return $this->_root; + } + + /** + * Returns a specific frame given its id + * + * @param string $id + * + * @return Frame + */ + public function get_frame($id) + { + return isset($this->_registry[$id]) ? $this->_registry[$id] : null; + } + + /** + * Returns a post-order iterator for all frames in the tree + * + * @return FrameTreeList|Frame[] + */ + public function get_frames() + { + return new FrameTreeList($this->_root); + } + + /** + * Builds the tree + */ + public function build_tree() + { + $html = $this->_dom->getElementsByTagName("html")->item(0); + if (is_null($html)) { + $html = $this->_dom->firstChild; + } + + if (is_null($html)) { + throw new Exception("Requested HTML document contains no data."); + } + + $this->fix_tables(); + + $this->_root = $this->_build_tree_r($html); + } + + /** + * Adds missing TBODYs around TR + */ + protected function fix_tables() + { + $xp = new DOMXPath($this->_dom); + + // Move table caption before the table + // FIXME find a better way to deal with it... + $captions = $xp->query("//table/caption"); + foreach ($captions as $caption) { + $table = $caption->parentNode; + $table->parentNode->insertBefore($caption, $table); + } + + $rows = $xp->query("//table/tr"); + foreach ($rows as $row) { + $tbody = $this->_dom->createElement("tbody"); + $tbody = $row->parentNode->insertBefore($tbody, $row); + $tbody->appendChild($row); + } + } + + // FIXME: temporary hack, preferably we will improve rendering of sequential #text nodes + /** + * Remove a child from a node + * + * Remove a child from a node. If the removed node results in two + * adjacent #text nodes then combine them. + * + * @param DONNode $node the current DOMNode being considered + * @param array $children an array of nodes that are the children of $node + * @param $index index from the $children array of the node to remove + */ + protected function _remove_node(DOMNode $node, array &$children, $index) + { + $child = $children[$index]; + $previousChild = $child->previousSibling; + $nextChild = $child->nextSibling; + $node->removeChild($child); + if (isset($previousChild, $nextChild)) { + if ($previousChild->nodeName === "#text" && $nextChild->nodeName === "#text") + { + $previousChild->nodeValue .= $nextChild->nodeValue; + $this->_remove_node($node, $children, $index+1); + } + } + array_splice($children, $index, 1); + } + + /** + * Recursively adds {@link Frame} objects to the tree + * + * Recursively build a tree of Frame objects based on a dom tree. + * No layout information is calculated at this time, although the + * tree may be adjusted (i.e. nodes and frames for generated content + * and images may be created). + * + * @param DOMNode $node the current DOMNode being considered + * + * @return Frame + */ + protected function _build_tree_r(DOMNode $node) + { + $frame = new Frame($node); + $id = $frame->get_id(); + $this->_registry[$id] = $frame; + + if (!$node->hasChildNodes()) { + return $frame; + } + + // Store the children in an array so that the tree can be modified + $children = array(); + $length = $node->childNodes->length; + for ($i = 0; $i < $length; $i++) { + $children[] = $node->childNodes->item($i); + } + $index = 0; + // INFO: We don't advance $index if a node is removed to avoid skipping nodes + while ($index < count($children)) { + $child = $children[$index]; + $nodeName = strtolower($child->nodeName); + + // Skip non-displaying nodes + if (in_array($nodeName, self::$HIDDEN_TAGS)) { + if ($nodeName !== "head" && $nodeName !== "style") { + $this->_remove_node($node, $children, $index); + } else { + $index++; + } + continue; + } + // Skip empty text nodes + if ($nodeName === "#text" && $child->nodeValue === "") { + $this->_remove_node($node, $children, $index); + continue; + } + // Skip empty image nodes + if ($nodeName === "img" && $child->getAttribute("src") === "") { + $this->_remove_node($node, $children, $index); + continue; + } + + if (is_object($child)) { + $frame->append_child($this->_build_tree_r($child), false); + } + $index++; + } + + return $frame; + } + + /** + * @param DOMNode $node + * @param DOMNode $new_node + * @param string $pos + * + * @return mixed + */ + public function insert_node(DOMNode $node, DOMNode $new_node, $pos) + { + if ($pos === "after" || !$node->firstChild) { + $node->appendChild($new_node); + } else { + $node->insertBefore($new_node, $node->firstChild); + } + + $this->_build_tree_r($new_node); + + $frame_id = $new_node->getAttribute("frame_id"); + $frame = $this->get_frame($frame_id); + + $parent_id = $node->getAttribute("frame_id"); + $parent = $this->get_frame($parent_id); + + if ($parent) { + if ($pos === "before") { + $parent->prepend_child($frame, false); + } else { + $parent->append_child($frame, false); + } + } + + return $frame_id; + } +} \ No newline at end of file diff --git a/library/vendor/dompdf/src/Frame/FrameTreeIterator.php b/library/vendor/dompdf/src/Frame/FrameTreeIterator.php new file mode 100644 index 000000000..ebca7e99f --- /dev/null +++ b/library/vendor/dompdf/src/Frame/FrameTreeIterator.php @@ -0,0 +1,96 @@ +_stack[] = $this->_root = $root; + $this->_num = 0; + } + + /** + * + */ + public function rewind() + { + $this->_stack = array($this->_root); + $this->_num = 0; + } + + /** + * @return bool + */ + public function valid() + { + return count($this->_stack) > 0; + } + + /** + * @return int + */ + public function key() + { + return $this->_num; + } + + /** + * @return Frame + */ + public function current() + { + return end($this->_stack); + } + + /** + * @return Frame + */ + public function next() + { + $b = end($this->_stack); + + // Pop last element + unset($this->_stack[key($this->_stack)]); + $this->_num++; + + // Push all children onto the stack in reverse order + if ($c = $b->get_last_child()) { + $this->_stack[] = $c; + while ($c = $c->get_prev_sibling()) { + $this->_stack[] = $c; + } + } + + return $b; + } +} + diff --git a/library/vendor/dompdf/src/Frame/FrameTreeList.php b/library/vendor/dompdf/src/Frame/FrameTreeList.php new file mode 100644 index 000000000..f8b996c68 --- /dev/null +++ b/library/vendor/dompdf/src/Frame/FrameTreeList.php @@ -0,0 +1,35 @@ +_root = $root; + } + + /** + * @return FrameTreeIterator + */ + public function getIterator() + { + return new FrameTreeIterator($this->_root); + } +} diff --git a/library/vendor/dompdf/src/FrameDecorator/AbstractFrameDecorator.php b/library/vendor/dompdf/src/FrameDecorator/AbstractFrameDecorator.php new file mode 100644 index 000000000..b574991e6 --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/AbstractFrameDecorator.php @@ -0,0 +1,808 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +/** + * Base AbstractFrameDecorator class + * + * @package dompdf + */ +abstract class AbstractFrameDecorator extends Frame +{ + const DEFAULT_COUNTER = "-dompdf-default-counter"; + + public $_counters = array(); // array([id] => counter_value) (for generated content) + + /** + * The root node of the DOM tree + * + * @var Frame + */ + protected $_root; + + /** + * The decorated frame + * + * @var Frame + */ + protected $_frame; + + /** + * AbstractPositioner object used to position this frame (Strategy pattern) + * + * @var AbstractPositioner + */ + protected $_positioner; + + /** + * Reflower object used to calculate frame dimensions (Strategy pattern) + * + * @var \Dompdf\FrameReflower\AbstractFrameReflower + */ + protected $_reflower; + + /** + * Reference to the current dompdf instance + * + * @var Dompdf + */ + protected $_dompdf; + + /** + * First block parent + * + * @var Block + */ + private $_block_parent; + + /** + * First positionned parent (position: relative | absolute | fixed) + * + * @var AbstractFrameDecorator + */ + private $_positionned_parent; + + /** + * Class constructor + * + * @param Frame $frame The decoration target + * @param Dompdf $dompdf The Dompdf object + */ + function __construct(Frame $frame, Dompdf $dompdf) + { + $this->_frame = $frame; + $this->_root = null; + $this->_dompdf = $dompdf; + $frame->set_decorator($this); + } + + /** + * "Destructor": foribly free all references held by this object + * + * @param bool $recursive if true, call dispose on all children + */ + function dispose($recursive = false) + { + if ($recursive) { + while ($child = $this->get_first_child()) { + $child->dispose(true); + } + } + + $this->_root = null; + unset($this->_root); + + $this->_frame->dispose(true); + $this->_frame = null; + unset($this->_frame); + + $this->_positioner = null; + unset($this->_positioner); + + $this->_reflower = null; + unset($this->_reflower); + } + + /** + * Return a copy of this frame with $node as its node + * + * @param DOMNode $node + * + * @return Frame + */ + function copy(DOMNode $node) + { + $frame = new Frame($node); + $frame->set_style(clone $this->_frame->get_original_style()); + + return Factory::decorate_frame($frame, $this->_dompdf, $this->_root); + } + + /** + * Create a deep copy: copy this node and all children + * + * @return Frame + */ + function deep_copy() + { + $node = $this->_frame->get_node(); + + if ($node instanceof DOMElement && $node->hasAttribute("id")) { + $node->setAttribute("data-dompdf-original-id", $node->getAttribute("id")); + $node->removeAttribute("id"); + } + + $frame = new Frame($node->cloneNode()); + $frame->set_style(clone $this->_frame->get_original_style()); + + $deco = Factory::decorate_frame($frame, $this->_dompdf, $this->_root); + + foreach ($this->get_children() as $child) { + $deco->append_child($child->deep_copy()); + } + + return $deco; + } + + /** + * Delegate calls to decorated frame object + */ + function reset() + { + $this->_frame->reset(); + + $this->_counters = array(); + + // Reset all children + foreach ($this->get_children() as $child) { + $child->reset(); + } + } + + // Getters ----------- + function get_id() + { + return $this->_frame->get_id(); + } + + /** + * @return Frame + */ + function get_frame() + { + return $this->_frame; + } + + /** + * @return DOMElement|DOMText + */ + function get_node() + { + return $this->_frame->get_node(); + } + + /** + * @return Style + */ + function get_style() + { + return $this->_frame->get_style(); + } + + /** + * @return Style + */ + function get_original_style() + { + return $this->_frame->get_original_style(); + } + + /** + * @param integer $i + * + * @return array|float + */ + function get_containing_block($i = null) + { + return $this->_frame->get_containing_block($i); + } + + /** + * @param integer $i + * + * @return array|float + */ + function get_position($i = null) + { + return $this->_frame->get_position($i); + } + + /** + * @return Dompdf + */ + function get_dompdf() + { + return $this->_dompdf; + } + + /** + * @return float + */ + function get_margin_height() + { + return $this->_frame->get_margin_height(); + } + + /** + * @return float + */ + function get_margin_width() + { + return $this->_frame->get_margin_width(); + } + + /** + * @return array + */ + function get_padding_box() + { + return $this->_frame->get_padding_box(); + } + + /** + * @return array + */ + function get_border_box() + { + return $this->_frame->get_border_box(); + } + + /** + * @param integer $id + */ + function set_id($id) + { + $this->_frame->set_id($id); + } + + /** + * @param Style $style + */ + function set_style(Style $style) + { + $this->_frame->set_style($style); + } + + /** + * @param float $x + * @param float $y + * @param float $w + * @param float $h + */ + function set_containing_block($x = null, $y = null, $w = null, $h = null) + { + $this->_frame->set_containing_block($x, $y, $w, $h); + } + + /** + * @param float $x + * @param float $y + */ + function set_position($x = null, $y = null) + { + $this->_frame->set_position($x, $y); + } + + /** + * @return string + */ + function __toString() + { + return $this->_frame->__toString(); + } + + /** + * @param Frame $child + * @param bool $update_node + */ + function prepend_child(Frame $child, $update_node = true) + { + while ($child instanceof AbstractFrameDecorator) { + $child = $child->_frame; + } + + $this->_frame->prepend_child($child, $update_node); + } + + /** + * @param Frame $child + * @param bool $update_node + */ + function append_child(Frame $child, $update_node = true) + { + while ($child instanceof AbstractFrameDecorator) { + $child = $child->_frame; + } + + $this->_frame->append_child($child, $update_node); + } + + /** + * @param Frame $new_child + * @param Frame $ref + * @param bool $update_node + */ + function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) + { + while ($new_child instanceof AbstractFrameDecorator) { + $new_child = $new_child->_frame; + } + + if ($ref instanceof AbstractFrameDecorator) { + $ref = $ref->_frame; + } + + $this->_frame->insert_child_before($new_child, $ref, $update_node); + } + + /** + * @param Frame $new_child + * @param Frame $ref + * @param bool $update_node + */ + function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) + { + while ($new_child instanceof AbstractFrameDecorator) { + $new_child = $new_child->_frame; + } + + while ($ref instanceof AbstractFrameDecorator) { + $ref = $ref->_frame; + } + + $this->_frame->insert_child_after($new_child, $ref, $update_node); + } + + /** + * @param Frame $child + * @param bool $update_node + * + * @return Frame + */ + function remove_child(Frame $child, $update_node = true) + { + while ($child instanceof AbstractFrameDecorator) { + $child = $child->_frame; + } + + return $this->_frame->remove_child($child, $update_node); + } + + /** + * @return AbstractFrameDecorator + */ + function get_parent() + { + $p = $this->_frame->get_parent(); + if ($p && $deco = $p->get_decorator()) { + while ($tmp = $deco->get_decorator()) { + $deco = $tmp; + } + + return $deco; + } else { + if ($p) { + return $p; + } + } + + return null; + } + + /** + * @return AbstractFrameDecorator + */ + function get_first_child() + { + $c = $this->_frame->get_first_child(); + if ($c && $deco = $c->get_decorator()) { + while ($tmp = $deco->get_decorator()) { + $deco = $tmp; + } + + return $deco; + } else { + if ($c) { + return $c; + } + } + + return null; + } + + /** + * @return AbstractFrameDecorator + */ + function get_last_child() + { + $c = $this->_frame->get_last_child(); + if ($c && $deco = $c->get_decorator()) { + while ($tmp = $deco->get_decorator()) { + $deco = $tmp; + } + + return $deco; + } else { + if ($c) { + return $c; + } + } + + return null; + } + + /** + * @return AbstractFrameDecorator + */ + function get_prev_sibling() + { + $s = $this->_frame->get_prev_sibling(); + if ($s && $deco = $s->get_decorator()) { + while ($tmp = $deco->get_decorator()) { + $deco = $tmp; + } + + return $deco; + } else { + if ($s) { + return $s; + } + } + + return null; + } + + /** + * @return AbstractFrameDecorator + */ + function get_next_sibling() + { + $s = $this->_frame->get_next_sibling(); + if ($s && $deco = $s->get_decorator()) { + while ($tmp = $deco->get_decorator()) { + $deco = $tmp; + } + + return $deco; + } else { + if ($s) { + return $s; + } + } + + return null; + } + + /** + * @return FrameTreeList + */ + function get_subtree() + { + return new FrameTreeList($this); + } + + function set_positioner(AbstractPositioner $posn) + { + $this->_positioner = $posn; + if ($this->_frame instanceof AbstractFrameDecorator) { + $this->_frame->set_positioner($posn); + } + } + + function set_reflower(AbstractFrameReflower $reflower) + { + $this->_reflower = $reflower; + if ($this->_frame instanceof AbstractFrameDecorator) { + $this->_frame->set_reflower($reflower); + } + } + + /** + * @return \Dompdf\FrameReflower\AbstractFrameReflower + */ + function get_reflower() + { + return $this->_reflower; + } + + /** + * @param Frame $root + */ + function set_root(Frame $root) + { + $this->_root = $root; + + if ($this->_frame instanceof AbstractFrameDecorator) { + $this->_frame->set_root($root); + } + } + + /** + * @return Page + */ + function get_root() + { + return $this->_root; + } + + /** + * @return Block + */ + function find_block_parent() + { + // Find our nearest block level parent + $p = $this->get_parent(); + + while ($p) { + if ($p->is_block()) { + break; + } + + $p = $p->get_parent(); + } + + return $this->_block_parent = $p; + } + + /** + * @return AbstractFrameDecorator + */ + function find_positionned_parent() + { + // Find our nearest relative positionned parent + $p = $this->get_parent(); + while ($p) { + if ($p->is_positionned()) { + break; + } + + $p = $p->get_parent(); + } + + if (!$p) { + $p = $this->_root->get_first_child(); // + } + + return $this->_positionned_parent = $p; + } + + /** + * split this frame at $child. + * The current frame is cloned and $child and all children following + * $child are added to the clone. The clone is then passed to the + * current frame's parent->split() method. + * + * @param Frame $child + * @param boolean $force_pagebreak + * + * @throws Exception + * @return void + */ + function split(Frame $child = null, $force_pagebreak = false) + { + // decrement any counters that were incremented on the current node, unless that node is the body + $style = $this->_frame->get_style(); + if ($this->_frame->get_node( + )->nodeName !== "body" && $style->counter_increment && ($decrement = $style->counter_increment) !== "none" + ) { + $this->decrement_counters($decrement); + } + + if (is_null($child)) { + // check for counter increment on :before content (always a child of the selected element @link AbstractFrameReflower::_set_content) + // this can push the current node to the next page before counter rules have bubbled up (but only if + // it's been rendered, thus the position check) + if (!$this->is_text_node() && $this->get_node()->hasAttribute("dompdf_before_frame_id")) { + foreach ($this->_frame->get_children() as $child) { + if ($this->get_node()->getAttribute("dompdf_before_frame_id") == $child->get_id( + ) && $child->get_position('x') !== null + ) { + $style = $child->get_style(); + if ($style->counter_increment && ($decrement = $style->counter_increment) !== "none") { + $this->decrement_counters($decrement); + } + } + } + } + $this->get_parent()->split($this, $force_pagebreak); + + return; + } + + if ($child->get_parent() !== $this) { + throw new Exception("Unable to split: frame is not a child of this one."); + } + + $node = $this->_frame->get_node(); + + if ($node instanceof DOMElement && $node->hasAttribute("id")) { + $node->setAttribute("data-dompdf-original-id", $node->getAttribute("id")); + $node->removeAttribute("id"); + } + + $split = $this->copy($node->cloneNode()); + $split->reset(); + $split->get_original_style()->text_indent = 0; + $split->_splitted = true; + + // The body's properties must be kept + if ($node->nodeName !== "body") { + // Style reset on the first and second parts + $style = $this->_frame->get_style(); + $style->margin_bottom = 0; + $style->padding_bottom = 0; + $style->border_bottom = 0; + + // second + $orig_style = $split->get_original_style(); + $orig_style->text_indent = 0; + $orig_style->margin_top = 0; + $orig_style->padding_top = 0; + $orig_style->border_top = 0; + $orig_style->page_break_before = "auto"; + } + + $this->get_parent()->insert_child_after($split, $this); + + // Add $frame and all following siblings to the new split node + $iter = $child; + while ($iter) { + $frame = $iter; + $iter = $iter->get_next_sibling(); + $frame->reset(); + $split->append_child($frame); + } + + $this->get_parent()->split($split, $force_pagebreak); + + // If this node resets a counter save the current value to use when rendering on the next page + if ($style->counter_reset && ($reset = $style->counter_reset) !== "none") { + $vars = preg_split('/\s+/', trim($reset), 2); + $split->_counters['__' . $vars[0]] = $this->lookup_counter_frame($vars[0])->_counters[$vars[0]]; + } + } + + function reset_counter($id = self::DEFAULT_COUNTER, $value = 0) + { + $this->get_parent()->_counters[$id] = intval($value); + } + + function decrement_counters($counters) + { + foreach ($counters as $id => $increment) { + $this->increment_counter($id, intval($increment) * -1); + } + } + + function increment_counters($counters) + { + foreach ($counters as $id => $increment) { + $this->increment_counter($id, intval($increment)); + } + } + + function increment_counter($id = self::DEFAULT_COUNTER, $increment = 1) + { + $counter_frame = $this->lookup_counter_frame($id); + + if ($counter_frame) { + if (!isset($counter_frame->_counters[$id])) { + $counter_frame->_counters[$id] = 0; + } + + $counter_frame->_counters[$id] += $increment; + } + } + + function lookup_counter_frame($id = self::DEFAULT_COUNTER) + { + $f = $this->get_parent(); + + while ($f) { + if (isset($f->_counters[$id])) { + return $f; + } + $fp = $f->get_parent(); + + if (!$fp) { + return $f; + } + + $f = $fp; + } + } + + // TODO: What version is the best : this one or the one in ListBullet ? + function counter_value($id = self::DEFAULT_COUNTER, $type = "decimal") + { + $type = mb_strtolower($type); + + if (!isset($this->_counters[$id])) { + $this->_counters[$id] = 0; + } + + $value = $this->_counters[$id]; + + switch ($type) { + default: + case "decimal": + return $value; + + case "decimal-leading-zero": + return str_pad($value, 2, "0", STR_PAD_LEFT); + + case "lower-roman": + return Helpers::dec2roman($value); + + case "upper-roman": + return mb_strtoupper(Helpers::dec2roman($value)); + + case "lower-latin": + case "lower-alpha": + return chr(($value % 26) + ord('a') - 1); + + case "upper-latin": + case "upper-alpha": + return chr(($value % 26) + ord('A') - 1); + + case "lower-greek": + return Helpers::unichr($value + 944); + + case "upper-greek": + return Helpers::unichr($value + 912); + } + } + + final function position() + { + $this->_positioner->position(); + } + + final function move($offset_x, $offset_y, $ignore_self = false) + { + $this->_positioner->move($offset_x, $offset_y, $ignore_self); + } + + final function reflow(Block $block = null) + { + // Uncomment this to see the frames before they're laid out, instead of + // during rendering. + //echo $this->_frame; flush(); + $this->_reflower->reflow($block); + } + + final function get_min_max_width() + { + return $this->_reflower->get_min_max_width(); + } +} diff --git a/library/vendor/dompdf/src/FrameDecorator/Block.php b/library/vendor/dompdf/src/FrameDecorator/Block.php new file mode 100644 index 000000000..c51fe2a80 --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/Block.php @@ -0,0 +1,252 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; +use Dompdf\LineBox; + +/** + * Decorates frames for block layout + * + * @access private + * @package dompdf + */ +class Block extends AbstractFrameDecorator +{ + /** + * Current line index + * + * @var int + */ + protected $_cl; + + /** + * The block's line boxes + * + * @var LineBox[] + */ + protected $_line_boxes; + + function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + + $this->_line_boxes = array(new LineBox($this)); + $this->_cl = 0; + } + + function reset() + { + parent::reset(); + + $this->_line_boxes = array(new LineBox($this)); + $this->_cl = 0; + } + + /** + * @return LineBox + */ + function get_current_line_box() + { + return $this->_line_boxes[$this->_cl]; + } + + /** + * @return integer + */ + function get_current_line_number() + { + return $this->_cl; + } + + /** + * @return LineBox[] + */ + function get_line_boxes() + { + return $this->_line_boxes; + } + + /** + * @param integer $i + */ + function clear_line($i) + { + if (isset($this->_line_boxes[$i])) { + unset($this->_line_boxes[$i]); + } + } + + /** + * @param Frame $frame + */ + function add_frame_to_line(Frame $frame) + { + if (!$frame->is_in_flow()) { + return; + } + + $style = $frame->get_style(); + + $frame->set_containing_line($this->_line_boxes[$this->_cl]); + + /* + // Adds a new line after a block, only if certain conditions are met + if ((($frame instanceof Inline && $frame->get_node()->nodeName !== "br") || + $frame instanceof Text && trim($frame->get_text())) && + ($frame->get_prev_sibling() && $frame->get_prev_sibling()->get_style()->display === "block" && + $this->_line_boxes[$this->_cl]->w > 0 )) { + + $this->maximize_line_height( $style->length_in_pt($style->line_height), $frame ); + $this->add_line(); + + // Add each child of the inline frame to the line individually + foreach ($frame->get_children() as $child) + $this->add_frame_to_line( $child ); + } + else*/ + + // Handle inline frames (which are effectively wrappers) + if ($frame instanceof Inline) { + + // Handle line breaks + if ($frame->get_node()->nodeName === "br") { + $this->maximize_line_height($style->length_in_pt($style->line_height), $frame); + $this->add_line(true); + } + + return; + } + + // Trim leading text if this is an empty line. Kinda a hack to put it here, + // but what can you do... + if ($this->get_current_line_box()->w == 0 && + $frame->is_text_node() && + !$frame->is_pre() + ) { + + $frame->set_text(ltrim($frame->get_text())); + $frame->recalculate_width(); + } + + $w = $frame->get_margin_width(); + + if ($w == 0) { + return; + } + + // Debugging code: + /* + Helpers::pre_r("\n

Adding frame to line:

"); + + // Helpers::pre_r("Me: " . $this->get_node()->nodeName . " (" . spl_object_hash($this->get_node()) . ")"); + // Helpers::pre_r("Node: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")"); + if ( $frame->is_text_node() ) + Helpers::pre_r('"'.$frame->get_node()->nodeValue.'"'); + + Helpers::pre_r("Line width: " . $this->_line_boxes[$this->_cl]->w); + Helpers::pre_r("Frame: " . get_class($frame)); + Helpers::pre_r("Frame width: " . $w); + Helpers::pre_r("Frame height: " . $frame->get_margin_height()); + Helpers::pre_r("Containing block width: " . $this->get_containing_block("w")); + */ + // End debugging + + $line = $this->_line_boxes[$this->_cl]; + if ($line->left + $line->w + $line->right + $w > $this->get_containing_block("w")) { + $this->add_line(); + } + + $frame->position(); + + $current_line = $this->_line_boxes[$this->_cl]; + $current_line->add_frame($frame); + + if ($frame->is_text_node()) { + $current_line->wc += count(preg_split("/\s+/", trim($frame->get_text()))); + } + + $this->increase_line_width($w); + + $this->maximize_line_height($frame->get_margin_height(), $frame); + } + + function remove_frames_from_line(Frame $frame) + { + // Search backwards through the lines for $frame + $i = $this->_cl; + $j = null; + + while ($i >= 0) { + if (($j = in_array($frame, $this->_line_boxes[$i]->get_frames(), true)) !== false) { + break; + } + + $i--; + } + + if ($j === false) { + return; + } + + // Remove $frame and all frames that follow + while ($j < count($this->_line_boxes[$i]->get_frames())) { + $frames = $this->_line_boxes[$i]->get_frames(); + $f = $frames[$j]; + $frames[$j] = null; + unset($frames[$j]); + $j++; + $this->_line_boxes[$i]->w -= $f->get_margin_width(); + } + + // Recalculate the height of the line + $h = 0; + foreach ($this->_line_boxes[$i]->get_frames() as $f) { + $h = max($h, $f->get_margin_height()); + } + + $this->_line_boxes[$i]->h = $h; + + // Remove all lines that follow + while ($this->_cl > $i) { + $this->_line_boxes[$this->_cl] = null; + unset($this->_line_boxes[$this->_cl]); + $this->_cl--; + } + } + + function increase_line_width($w) + { + $this->_line_boxes[$this->_cl]->w += $w; + } + + function maximize_line_height($val, Frame $frame) + { + if ($val > $this->_line_boxes[$this->_cl]->h) { + $this->_line_boxes[$this->_cl]->tallest_frame = $frame; + $this->_line_boxes[$this->_cl]->h = $val; + } + } + + function add_line($br = false) + { + +// if ( $this->_line_boxes[$this->_cl]["h"] == 0 ) //count($this->_line_boxes[$i]["frames"]) == 0 || +// return; + + $this->_line_boxes[$this->_cl]->br = $br; + $y = $this->_line_boxes[$this->_cl]->y + $this->_line_boxes[$this->_cl]->h; + + $new_line = new LineBox($this, $y); + + $this->_line_boxes[++$this->_cl] = $new_line; + } + + //........................................................................ +} diff --git a/library/vendor/dompdf/src/FrameDecorator/Image.php b/library/vendor/dompdf/src/FrameDecorator/Image.php new file mode 100644 index 000000000..22a308a4a --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/Image.php @@ -0,0 +1,90 @@ + + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; +use Dompdf\FontMetrics; +use Dompdf\Image\Cache; + +/** + * Decorates frames for image layout and rendering + * + * @package dompdf + */ +class Image extends AbstractFrameDecorator +{ + + /** + * The path to the image file (note that remote images are + * downloaded locally to Options:tempDir). + * + * @var string + */ + protected $_image_url; + + /** + * The image's file error message + * + * @var string + */ + protected $_image_msg; + + /** + * Class constructor + * + * @param Frame $frame the frame to decorate + * @param DOMPDF $dompdf the document's dompdf object (required to resolve relative & remote urls) + */ + function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + $url = $frame->get_node()->getAttribute("src"); + + $debug_png = $dompdf->get_option("debug_png"); + if ($debug_png) print '[__construct ' . $url . ']'; + + list($this->_image_url, /*$type*/, $this->_image_msg) = Cache::resolve_url( + $url, + $dompdf->get_protocol(), + $dompdf->get_host(), + $dompdf->get_base_path(), + $dompdf + ); + + if (Cache::is_broken($this->_image_url) && + $alt = $frame->get_node()->getAttribute("alt") + ) { + $style = $frame->get_style(); + $style->width = (4 / 3) * $dompdf->getFontMetrics()->getTextWidth($alt, $style->font_family, $style->font_size, $style->word_spacing); + $style->height = $dompdf->getFontMetrics()->getFontHeight($style->font_family, $style->font_size); + } + } + + /** + * Return the image's url + * + * @return string The url of this image + */ + function get_image_url() + { + return $this->_image_url; + } + + /** + * Return the image's error message + * + * @return string The image's error message + */ + function get_image_msg() + { + return $this->_image_msg; + } + +} diff --git a/library/vendor/dompdf/src/FrameDecorator/Inline.php b/library/vendor/dompdf/src/FrameDecorator/Inline.php new file mode 100644 index 000000000..05920f493 --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/Inline.php @@ -0,0 +1,92 @@ + + * @author Helmut Tischer + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; +use Dompdf\Exception; + +/** + * Decorates frames for inline layout + * + * @access private + * @package dompdf + */ +class Inline extends AbstractFrameDecorator +{ + + function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + } + + function split(Frame $frame = null, $force_pagebreak = false) + { + if (is_null($frame)) { + $this->get_parent()->split($this, $force_pagebreak); + return; + } + + if ($frame->get_parent() !== $this) { + throw new Exception("Unable to split: frame is not a child of this one."); + } + + $node = $this->_frame->get_node(); + + if ($node instanceof DOMElement && $node->hasAttribute("id")) { + $node->setAttribute("data-dompdf-original-id", $node->getAttribute("id")); + $node->removeAttribute("id"); + } + + $split = $this->copy($node->cloneNode()); + $this->get_parent()->insert_child_after($split, $this); + + // Unset the current node's right style properties + $style = $this->_frame->get_style(); + $style->margin_right = 0; + $style->padding_right = 0; + $style->border_right_width = 0; + + // Unset the split node's left style properties since we don't want them + // to propagate + $style = $split->get_style(); + $style->margin_left = 0; + $style->padding_left = 0; + $style->border_left_width = 0; + + //On continuation of inline element on next line, + //don't repeat non-vertically repeatble background images + //See e.g. in testcase image_variants, long desriptions + if (($url = $style->background_image) && $url !== "none" + && ($repeat = $style->background_repeat) && $repeat !== "repeat" && $repeat !== "repeat-y" + ) { + $style->background_image = "none"; + } + + // Add $frame and all following siblings to the new split node + $iter = $frame; + while ($iter) { + $frame = $iter; + $iter = $iter->get_next_sibling(); + $frame->reset(); + $split->append_child($frame); + } + + $page_breaks = array("always", "left", "right"); + $frame_style = $frame->get_style(); + if ($force_pagebreak || + in_array($frame_style->page_break_before, $page_breaks) || + in_array($frame_style->page_break_after, $page_breaks) + ) { + + $this->get_parent()->split($split, true); + } + } + +} diff --git a/library/vendor/dompdf/src/FrameDecorator/ListBullet.php b/library/vendor/dompdf/src/FrameDecorator/ListBullet.php new file mode 100644 index 000000000..2f9154625 --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/ListBullet.php @@ -0,0 +1,75 @@ + + * @author Helmut Tischer + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; + +/** + * Decorates frames for list bullet rendering + * + * @package dompdf + */ +class ListBullet extends AbstractFrameDecorator +{ + + const BULLET_PADDING = 1; // Distance from bullet to text in pt + // As fraction of font size (including descent). See also DECO_THICKNESS. + const BULLET_THICKNESS = 0.04; // Thickness of bullet outline. Screen: 0.08, print: better less, e.g. 0.04 + const BULLET_DESCENT = 0.3; //descent of font below baseline. Todo: Guessed for now. + const BULLET_SIZE = 0.35; // bullet diameter. For now 0.5 of font_size without descent. + + static $BULLET_TYPES = array("disc", "circle", "square"); + + //........................................................................ + + function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + } + + function get_margin_width() + { + $style = $this->_frame->get_style(); + + // Small hack to prevent extra indenting of list text on list_style_position === "inside" + // and on suppressed bullet + if ($style->list_style_position === "outside" || + $style->list_style_type === "none" + ) { + return 0; + } + + return $style->get_font_size() * self::BULLET_SIZE + 2 * self::BULLET_PADDING; + } + + //hits only on "inset" lists items, to increase height of box + function get_margin_height() + { + $style = $this->_frame->get_style(); + + if ($style->list_style_type === "none") { + return 0; + } + + return $style->get_font_size() * self::BULLET_SIZE + 2 * self::BULLET_PADDING; + } + + function get_width() + { + return $this->get_margin_height(); + } + + function get_height() + { + return $this->get_margin_height(); + } + + //........................................................................ +} diff --git a/library/vendor/dompdf/src/FrameDecorator/ListBulletImage.php b/library/vendor/dompdf/src/FrameDecorator/ListBulletImage.php new file mode 100644 index 000000000..159eaec13 --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/ListBulletImage.php @@ -0,0 +1,155 @@ + + * @author Helmut Tischer + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; +use Dompdf\Helpers; + +/** + * Decorates frames for list bullets with custom images + * + * @package dompdf + */ +class ListBulletImage extends AbstractFrameDecorator +{ + + /** + * The underlying image frame + * + * @var Image + */ + protected $_img; + + /** + * The image's width in pixels + * + * @var int + */ + protected $_width; + + /** + * The image's height in pixels + * + * @var int + */ + protected $_height; + + /** + * Class constructor + * + * @param Frame $frame the bullet frame to decorate + * @param Dompdf $dompdf the document's dompdf object + */ + function __construct(Frame $frame, Dompdf $dompdf) + { + $style = $frame->get_style(); + $url = $style->list_style_image; + $frame->get_node()->setAttribute("src", $url); + $this->_img = new Image($frame, $dompdf); + parent::__construct($this->_img, $dompdf); + list($width, $height) = Helpers::dompdf_getimagesize($this->_img->get_image_url(), $dompdf->getHttpContext()); + + // Resample the bullet image to be consistent with 'auto' sized images + // See also Image::get_min_max_width + // Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary. + $dpi = $this->_dompdf->get_option("dpi"); + $this->_width = ((float)rtrim($width, "px") * 72) / $dpi; + $this->_height = ((float)rtrim($height, "px") * 72) / $dpi; + + //If an image is taller as the containing block/box, the box should be extended. + //Neighbour elements are overwriting the overlapping image areas. + //Todo: Where can the box size be extended? + //Code below has no effect. + //See block_frame_reflower _calculate_restricted_height + //See generated_frame_reflower, Dompdf:render() "list-item", "-dompdf-list-bullet"S. + //Leave for now + //if ($style->min_height < $this->_height ) { + // $style->min_height = $this->_height; + //} + //$style->height = "auto"; + } + + /** + * Return the bullet's width + * + * @return int + */ + function get_width() + { + //ignore image width, use same width as on predefined bullet ListBullet + //for proper alignment of bullet image and text. Allow image to not fitting on left border. + //This controls the distance between bullet image and text + //return $this->_width; + return $this->_frame->get_style()->get_font_size() * ListBullet::BULLET_SIZE + + 2 * ListBullet::BULLET_PADDING; + } + + /** + * Return the bullet's height + * + * @return int + */ + function get_height() + { + //based on image height + return $this->_height; + } + + /** + * Override get_margin_width + * + * @return int + */ + function get_margin_width() + { + //ignore image width, use same width as on predefined bullet ListBullet + //for proper alignment of bullet image and text. Allow image to not fitting on left border. + //This controls the extra indentation of text to make room for the bullet image. + //Here use actual image size, not predefined bullet size + //return $this->_frame->get_style()->get_font_size()*ListBullet::BULLET_SIZE + + // 2 * ListBullet::BULLET_PADDING; + + // Small hack to prevent indenting of list text + // Image Might not exist, then position like on list_bullet_frame_decorator fallback to none. + if ($this->_frame->get_style()->list_style_position === "outside" || + $this->_width == 0 + ) + return 0; + //This aligns the "inside" image position with the text. + //The text starts to the right of the image. + //Between the image and the text there is an added margin of image width. + //Where this comes from is unknown. + //The corresponding ListBullet sets a smaller margin. bullet size? + return $this->_width + 2 * ListBullet::BULLET_PADDING; + } + + /** + * Override get_margin_height() + * + * @return int + */ + function get_margin_height() + { + //Hits only on "inset" lists items, to increase height of box + //based on image height + return $this->_height + 2 * ListBullet::BULLET_PADDING; + } + + /** + * Return image url + * + * @return string + */ + function get_image_url() + { + return $this->_img->get_image_url(); + } + +} diff --git a/library/vendor/dompdf/src/FrameDecorator/NullFrameDecorator.php b/library/vendor/dompdf/src/FrameDecorator/NullFrameDecorator.php new file mode 100644 index 000000000..c9f2d94c4 --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/NullFrameDecorator.php @@ -0,0 +1,31 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; + +/** + * Dummy decorator + * + * @package dompdf + */ +class NullFrameDecorator extends AbstractFrameDecorator +{ + + function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + $style = $this->_frame->get_style(); + $style->width = 0; + $style->height = 0; + $style->margin = 0; + $style->padding = 0; + } + +} diff --git a/library/vendor/dompdf/src/FrameDecorator/Page.php b/library/vendor/dompdf/src/FrameDecorator/Page.php new file mode 100644 index 000000000..0310f60c2 --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/Page.php @@ -0,0 +1,658 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Css\Style; +use Dompdf\Dompdf; +use Dompdf\Helpers; +use Dompdf\Frame; +use Dompdf\Renderer; + +/** + * Decorates frames for page layout + * + * @access private + * @package dompdf + */ +class Page extends AbstractFrameDecorator +{ + + /** + * y value of bottom page margin + * + * @var float + */ + protected $_bottom_page_margin; + + /** + * Flag indicating page is full. + * + * @var bool + */ + protected $_page_full; + + /** + * Number of tables currently being reflowed + * + * @var int + */ + protected $_in_table; + + /** + * The pdf renderer + * + * @var Renderer + */ + protected $_renderer; + + /** + * This page's floating frames + * + * @var array + */ + protected $_floating_frames = array(); + + //........................................................................ + + /** + * Class constructor + * + * @param Frame $frame the frame to decorate + * @param Dompdf $dompdf + */ + function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + $this->_page_full = false; + $this->_in_table = 0; + $this->_bottom_page_margin = null; + } + + /** + * Set the renderer used for this pdf + * + * @param Renderer $renderer the renderer to use + */ + function set_renderer($renderer) + { + $this->_renderer = $renderer; + } + + /** + * Return the renderer used for this pdf + * + * @return Renderer + */ + function get_renderer() + { + return $this->_renderer; + } + + /** + * Set the frame's containing block. Overridden to set $this->_bottom_page_margin. + * + * @param float $x + * @param float $y + * @param float $w + * @param float $h + */ + function set_containing_block($x = null, $y = null, $w = null, $h = null) + { + parent::set_containing_block($x, $y, $w, $h); + //$w = $this->get_containing_block("w"); + if (isset($h)) { + $this->_bottom_page_margin = $h; + } // - $this->_frame->get_style()->length_in_pt($this->_frame->get_style()->margin_bottom, $w); + } + + /** + * Returns true if the page is full and is no longer accepting frames. + * + * @return bool + */ + function is_full() + { + return $this->_page_full; + } + + /** + * Start a new page by resetting the full flag. + */ + function next_page() + { + $this->_floating_frames = array(); + $this->_renderer->new_page(); + $this->_page_full = false; + } + + /** + * Indicate to the page that a table is currently being reflowed. + */ + function table_reflow_start() + { + $this->_in_table++; + } + + /** + * Indicate to the page that table reflow is finished. + */ + function table_reflow_end() + { + $this->_in_table--; + } + + /** + * Return whether we are currently in a nested table or not + * + * @return bool + */ + function in_nested_table() + { + return $this->_in_table > 1; + } + + /** + * Check if a forced page break is required before $frame. This uses the + * frame's page_break_before property as well as the preceeding frame's + * page_break_after property. + * + * @link http://www.w3.org/TR/CSS21/page.html#forced + * + * @param Frame $frame the frame to check + * + * @return bool true if a page break occured + */ + function check_forced_page_break(Frame $frame) + { + + // Skip check if page is already split + if ($this->_page_full) { + return null; + } + + $block_types = array("block", "list-item", "table", "inline"); + $page_breaks = array("always", "left", "right"); + + $style = $frame->get_style(); + + if (!in_array($style->display, $block_types)) { + return false; + } + + // Find the previous block-level sibling + $prev = $frame->get_prev_sibling(); + + while ($prev && !in_array($prev->get_style()->display, $block_types)) { + $prev = $prev->get_prev_sibling(); + } + + + if (in_array($style->page_break_before, $page_breaks)) { + + // Prevent cascading splits + $frame->split(null, true); + // We have to grab the style again here because split() resets + // $frame->style to the frame's orignal style. + $frame->get_style()->page_break_before = "auto"; + $this->_page_full = true; + + return true; + } + + if ($prev && in_array($prev->get_style()->page_break_after, $page_breaks)) { + // Prevent cascading splits + $frame->split(null, true); + $prev->get_style()->page_break_after = "auto"; + $this->_page_full = true; + + return true; + } + + if ($prev && $prev->get_last_child() && $frame->get_node()->nodeName != "body") { + $prev_last_child = $prev->get_last_child(); + if (in_array($prev_last_child->get_style()->page_break_after, $page_breaks)) { + $frame->split(null, true); + $prev_last_child->get_style()->page_break_after = "auto"; + $this->_page_full = true; + + return true; + } + } + + + return false; + } + + /** + * Determine if a page break is allowed before $frame + * http://www.w3.org/TR/CSS21/page.html#allowed-page-breaks + * + * In the normal flow, page breaks can occur at the following places: + * + * 1. In the vertical margin between block boxes. When a page + * break occurs here, the used values of the relevant + * 'margin-top' and 'margin-bottom' properties are set to '0'. + * 2. Between line boxes inside a block box. + * + * These breaks are subject to the following rules: + * + * * Rule A: Breaking at (1) is allowed only if the + * 'page-break-after' and 'page-break-before' properties of + * all the elements generating boxes that meet at this margin + * allow it, which is when at least one of them has the value + * 'always', 'left', or 'right', or when all of them are + * 'auto'. + * + * * Rule B: However, if all of them are 'auto' and the + * nearest common ancestor of all the elements has a + * 'page-break-inside' value of 'avoid', then breaking here is + * not allowed. + * + * * Rule C: Breaking at (2) is allowed only if the number of + * line boxes between the break and the start of the enclosing + * block box is the value of 'orphans' or more, and the number + * of line boxes between the break and the end of the box is + * the value of 'widows' or more. + * + * * Rule D: In addition, breaking at (2) is allowed only if + * the 'page-break-inside' property is 'auto'. + * + * If the above doesn't provide enough break points to keep + * content from overflowing the page boxes, then rules B and D are + * dropped in order to find additional breakpoints. + * + * If that still does not lead to sufficient break points, rules A + * and C are dropped as well, to find still more break points. + * + * We will also allow breaks between table rows. However, when + * splitting a table, the table headers should carry over to the + * next page (but they don't yet). + * + * @param Frame $frame the frame to check + * + * @return bool true if a break is allowed, false otherwise + */ + protected function _page_break_allowed(Frame $frame) + { + + $block_types = array("block", "list-item", "table", "-dompdf-image"); + Helpers::dompdf_debug("page-break", "_page_break_allowed(" . $frame->get_node()->nodeName . ")"); + $display = $frame->get_style()->display; + + // Block Frames (1): + if (in_array($display, $block_types)) { + + // Avoid breaks within table-cells + if ($this->_in_table) { + Helpers::dompdf_debug("page-break", "In table: " . $this->_in_table); + + return false; + } + + // Rules A & B + + if ($frame->get_style()->page_break_before === "avoid") { + Helpers::dompdf_debug("page-break", "before: avoid"); + + return false; + } + + // Find the preceeding block-level sibling + $prev = $frame->get_prev_sibling(); + while ($prev && !in_array($prev->get_style()->display, $block_types)) { + $prev = $prev->get_prev_sibling(); + } + + // Does the previous element allow a page break after? + if ($prev && $prev->get_style()->page_break_after === "avoid") { + Helpers::dompdf_debug("page-break", "after: avoid"); + + return false; + } + + // If both $prev & $frame have the same parent, check the parent's + // page_break_inside property. + $parent = $frame->get_parent(); + if ($prev && $parent && $parent->get_style()->page_break_inside === "avoid") { + Helpers::dompdf_debug("page-break", "parent inside: avoid"); + + return false; + } + + // To prevent cascading page breaks when a top-level element has + // page-break-inside: avoid, ensure that at least one frame is + // on the page before splitting. + if ($parent->get_node()->nodeName === "body" && !$prev) { + // We are the body's first child + Helpers::dompdf_debug("page-break", "Body's first child."); + + return false; + } + + // If the frame is the first block-level frame, use the value from + // $frame's parent instead. + if (!$prev && $parent) { + return $this->_page_break_allowed($parent); + } + + Helpers::dompdf_debug("page-break", "block: break allowed"); + + return true; + + } // Inline frames (2): + else { + if (in_array($display, Style::$INLINE_TYPES)) { + + // Avoid breaks within table-cells + if ($this->_in_table) { + Helpers::dompdf_debug("page-break", "In table: " . $this->_in_table); + + return false; + } + + // Rule C + $block_parent = $frame->find_block_parent(); + if (count($block_parent->get_line_boxes()) < $frame->get_style()->orphans) { + Helpers::dompdf_debug("page-break", "orphans"); + + return false; + } + + // FIXME: Checking widows is tricky without having laid out the + // remaining line boxes. Just ignore it for now... + + // Rule D + $p = $block_parent; + while ($p) { + if ($p->get_style()->page_break_inside === "avoid") { + Helpers::dompdf_debug("page-break", "parent->inside: avoid"); + + return false; + } + $p = $p->find_block_parent(); + } + + // To prevent cascading page breaks when a top-level element has + // page-break-inside: avoid, ensure that at least one frame with + // some content is on the page before splitting. + $prev = $frame->get_prev_sibling(); + while ($prev && ($prev->is_text_node() && trim($prev->get_node()->nodeValue) == "")) { + $prev = $prev->get_prev_sibling(); + } + + if ($block_parent->get_node()->nodeName === "body" && !$prev) { + // We are the body's first child + Helpers::dompdf_debug("page-break", "Body's first child."); + + return false; + } + + // Skip breaks on empty text nodes + if ($frame->is_text_node() && + $frame->get_node()->nodeValue == "" + ) { + return false; + } + + Helpers::dompdf_debug("page-break", "inline: break allowed"); + + return true; + + // Table-rows + } else { + if ($display === "table-row") { + + // Simply check if the parent table's page_break_inside property is + // not 'avoid' + $p = Table::find_parent_table($frame); + + while ($p) { + if ($p->get_style()->page_break_inside === "avoid") { + Helpers::dompdf_debug("page-break", "parent->inside: avoid"); + + return false; + } + $p = $p->find_block_parent(); + } + + // Avoid breaking after the first row of a table + if ($p && $p->get_first_child() === $frame) { + Helpers::dompdf_debug("page-break", "table: first-row"); + + return false; + } + + // If this is a nested table, prevent the page from breaking + if ($this->_in_table > 1) { + Helpers::dompdf_debug("page-break", "table: nested table"); + + return false; + } + + Helpers::dompdf_debug("page-break", "table-row/row-groups: break allowed"); + + return true; + + } else { + if (in_array($display, Table::$ROW_GROUPS)) { + + // Disallow breaks at row-groups: only split at row boundaries + return false; + + } else { + + Helpers::dompdf_debug("page-break", "? " . $frame->get_style()->display . ""); + + return false; + } + } + } + } + + } + + /** + * Check if $frame will fit on the page. If the frame does not fit, + * the frame tree is modified so that a page break occurs in the + * correct location. + * + * @param Frame $frame the frame to check + * + * @return Frame the frame following the page break + */ + function check_page_break(Frame $frame) + { + // Do not split if we have already or if the frame was already + // pushed to the next page (prevents infinite loops) + if ($this->_page_full || $frame->_already_pushed) { + return false; + } + + // If the frame is absolute of fixed it shouldn't break + $p = $frame; + do { + if ($p->is_absolute()) + return false; + } while ($p = $p->get_parent()); + + $margin_height = $frame->get_margin_height(); + + // FIXME If the row is taller than the page and + // if it the first of the page, we don't break + if ($frame->get_style()->display === "table-row" && + !$frame->get_prev_sibling() && + $margin_height > $this->get_margin_height() + ) + return false; + + // Determine the frame's maximum y value + $max_y = $frame->get_position("y") + $margin_height; + + // If a split is to occur here, then the bottom margins & paddings of all + // parents of $frame must fit on the page as well: + $p = $frame->get_parent(); + while ($p) { + $style = $p->get_style(); + $max_y += $style->length_in_pt( + array( + $style->margin_bottom, + $style->padding_bottom, + $style->border_bottom_width + ) + ); + $p = $p->get_parent(); + } + + + // Check if $frame flows off the page + if ($max_y <= $this->_bottom_page_margin) + // no: do nothing + return false; + + Helpers::dompdf_debug("page-break", "check_page_break"); + Helpers::dompdf_debug("page-break", "in_table: " . $this->_in_table); + + // yes: determine page break location + $iter = $frame; + $flg = false; + + $in_table = $this->_in_table; + + Helpers::dompdf_debug("page-break", "Starting search"); + while ($iter) { + // echo "\nbacktrack: " .$iter->get_node()->nodeName ." ".spl_object_hash($iter->get_node()). ""; + if ($iter === $this) { + Helpers::dompdf_debug("page-break", "reached root."); + // We've reached the root in our search. Just split at $frame. + break; + } + + if ($this->_page_break_allowed($iter)) { + Helpers::dompdf_debug("page-break", "break allowed, splitting."); + $iter->split(null, true); + $this->_page_full = true; + $this->_in_table = $in_table; + $frame->_already_pushed = true; + + return true; + } + + if (!$flg && $next = $iter->get_last_child()) { + Helpers::dompdf_debug("page-break", "following last child."); + + if ($next->is_table()) + $this->_in_table++; + + $iter = $next; + continue; + } + + if ($next = $iter->get_prev_sibling()) { + Helpers::dompdf_debug("page-break", "following prev sibling."); + + if ($next->is_table() && !$iter->is_table()) + $this->_in_table++; + + else if (!$next->is_table() && $iter->is_table()) + $this->_in_table--; + + $iter = $next; + $flg = false; + continue; + } + + if ($next = $iter->get_parent()) { + Helpers::dompdf_debug("page-break", "following parent."); + + if ($iter->is_table()) + $this->_in_table--; + + $iter = $next; + $flg = true; + continue; + } + + break; + } + + $this->_in_table = $in_table; + + // No valid page break found. Just break at $frame. + Helpers::dompdf_debug("page-break", "no valid break found, just splitting."); + + // If we are in a table, backtrack to the nearest top-level table row + if ($this->_in_table) { + $iter = $frame; + while ($iter && $iter->get_style()->display !== "table-row" && $iter->get_style()->display !== 'table-row-group') + $iter = $iter->get_parent(); + + $iter->split(null, true); + } else { + $frame->split(null, true); + } + + $this->_page_full = true; + $frame->_already_pushed = true; + + return true; + } + + //........................................................................ + + function split(Frame $frame = null, $force_pagebreak = false) + { + // Do nothing + } + + /** + * Add a floating frame + * + * @param Frame $frame + * + * @return void + */ + function add_floating_frame(Frame $frame) + { + array_unshift($this->_floating_frames, $frame); + } + + /** + * @return Frame[] + */ + function get_floating_frames() + { + return $this->_floating_frames; + } + + public function remove_floating_frame($key) + { + unset($this->_floating_frames[$key]); + } + + public function get_lowest_float_offset(Frame $child) + { + $style = $child->get_style(); + $side = $style->clear; + $float = $style->float; + + $y = 0; + + foreach ($this->_floating_frames as $key => $frame) { + if ($side === "both" || $frame->get_style()->float === $side) { + $y = max($y, $frame->get_position("y") + $frame->get_margin_height()); + + if ($float !== "none") { + $this->remove_floating_frame($key); + } + } + } + + return $y; + } + +} diff --git a/library/vendor/dompdf/src/FrameDecorator/Table.php b/library/vendor/dompdf/src/FrameDecorator/Table.php new file mode 100644 index 000000000..bdbead59e --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/Table.php @@ -0,0 +1,370 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Cellmap; +use DOMNode; +use Dompdf\Dompdf; +use Dompdf\Frame; +use Dompdf\Frame\Factory; + +/** + * Decorates Frames for table layout + * + * @package dompdf + */ +class Table extends AbstractFrameDecorator +{ + public static $VALID_CHILDREN = array( + "table-row-group", + "table-row", + "table-header-group", + "table-footer-group", + "table-column", + "table-column-group", + "table-caption", + "table-cell" + ); + + public static $ROW_GROUPS = array( + 'table-row-group', + 'table-header-group', + 'table-footer-group' + ); + + /** + * The Cellmap object for this table. The cellmap maps table cells + * to rows and columns, and aids in calculating column widths. + * + * @var \Dompdf\Cellmap + */ + protected $_cellmap; + + /** + * The minimum width of the table, in pt + * + * @var float + */ + protected $_min_width; + + /** + * The maximum width of the table, in pt + * + * @var float + */ + protected $_max_width; + + /** + * Table header rows. Each table header is duplicated when a table + * spans pages. + * + * @var array + */ + protected $_headers; + + /** + * Table footer rows. Each table footer is duplicated when a table + * spans pages. + * + * @var array + */ + protected $_footers; + + /** + * Class constructor + * + * @param Frame $frame the frame to decorate + * @param Dompdf $dompdf + */ + public function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + $this->_cellmap = new Cellmap($this); + + if ($frame->get_style()->table_layout === "fixed") { + $this->_cellmap->set_layout_fixed(true); + } + + $this->_min_width = null; + $this->_max_width = null; + $this->_headers = array(); + $this->_footers = array(); + } + + + public function reset() + { + parent::reset(); + $this->_cellmap->reset(); + $this->_min_width = null; + $this->_max_width = null; + $this->_headers = array(); + $this->_footers = array(); + $this->_reflower->reset(); + } + + //........................................................................ + + /** + * split the table at $row. $row and all subsequent rows will be + * added to the clone. This method is overidden in order to remove + * frames from the cellmap properly. + * + * @param Frame $child + * @param bool $force_pagebreak + * + * @return void + */ + public function split(Frame $child = null, $force_pagebreak = false) + { + if (is_null($child)) { + parent::split(); + + return; + } + + // If $child is a header or if it is the first non-header row, do + // not duplicate headers, simply move the table to the next page. + if (count($this->_headers) && !in_array($child, $this->_headers, true) && + !in_array($child->get_prev_sibling(), $this->_headers, true) + ) { + + $first_header = null; + + // Insert copies of the table headers before $child + foreach ($this->_headers as $header) { + + $new_header = $header->deep_copy(); + + if (is_null($first_header)) { + $first_header = $new_header; + } + + $this->insert_child_before($new_header, $child); + } + + parent::split($first_header); + + } elseif (in_array($child->get_style()->display, self::$ROW_GROUPS)) { + + // Individual rows should have already been handled + parent::split($child); + + } else { + + $iter = $child; + + while ($iter) { + $this->_cellmap->remove_row($iter); + $iter = $iter->get_next_sibling(); + } + + parent::split($child); + } + } + + /** + * Return a copy of this frame with $node as its node + * + * @param DOMNode $node + * + * @return Frame + */ + public function copy(DOMNode $node) + { + $deco = parent::copy($node); + + // In order to keep columns' widths through pages + $deco->_cellmap->set_columns($this->_cellmap->get_columns()); + $deco->_cellmap->lock_columns(); + + return $deco; + } + + /** + * Static function to locate the parent table of a frame + * + * @param Frame $frame + * + * @return Table the table that is an ancestor of $frame + */ + public static function find_parent_table(Frame $frame) + { + + while ($frame = $frame->get_parent()) + if ($frame->is_table()) + break; + + return $frame; + } + + /** + * Return this table's Cellmap + * + * @return \Dompdf\Cellmap + */ + public function get_cellmap() + { + return $this->_cellmap; + } + + /** + * Return the minimum width of this table + * + * @return float + */ + public function get_min_width() + { + return $this->_min_width; + } + + /** + * Return the maximum width of this table + * + * @return float + */ + public function get_max_width() + { + return $this->_max_width; + } + + /** + * Set the minimum width of the table + * + * @param float $width the new minimum width + */ + public function set_min_width($width) + { + $this->_min_width = $width; + } + + /** + * Set the maximum width of the table + * + * @param float $width the new maximum width + */ + public function set_max_width($width) + { + $this->_max_width = $width; + } + + /** + * Restructure tree so that the table has the correct structure. + * Invalid children (i.e. all non-table-rows) are moved below the + * table. + */ + public function normalise() + { + // Store frames generated by invalid tags and move them outside the table + $erroneous_frames = array(); + $anon_row = false; + $iter = $this->get_first_child(); + while ($iter) { + $child = $iter; + $iter = $iter->get_next_sibling(); + + $display = $child->get_style()->display; + + if ($anon_row) { + + if ($display === "table-row") { + // Add the previous anonymous row + $this->insert_child_before($table_row, $child); + + $table_row->normalise(); + $child->normalise(); + $anon_row = false; + continue; + } + + // add the child to the anonymous row + $table_row->append_child($child); + continue; + + } else { + + if ($display === "table-row") { + $child->normalise(); + continue; + } + + if ($display === "table-cell") { + // Create an anonymous table row + $tr = $this->get_node()->ownerDocument->createElement("tr"); + + $frame = new Frame($tr); + + $css = $this->get_style()->get_stylesheet(); + $style = $css->create_style(); + $style->inherit($this->get_style()); + + // Lookup styles for tr tags. If the user wants styles to work + // better, they should make the tr explicit... I'm not going to + // try to guess what they intended. + if ($tr_style = $css->lookup("tr")) { + $style->merge($tr_style); + } + + // Okay, I have absolutely no idea why I need this clone here, but + // if it's omitted, php (as of 2004-07-28) segfaults. + $frame->set_style(clone $style); + $table_row = Factory::decorate_frame($frame, $this->_dompdf, $this->_root); + + // Add the cell to the row + $table_row->append_child($child); + + $anon_row = true; + continue; + } + + if (!in_array($display, self::$VALID_CHILDREN)) { + $erroneous_frames[] = $child; + continue; + } + + // Normalise other table parts (i.e. row groups) + foreach ($child->get_children() as $grandchild) { + if ($grandchild->get_style()->display === "table-row") { + $grandchild->normalise(); + } + } + + // Add headers and footers + if ($display === "table-header-group") { + $this->_headers[] = $child; + } elseif ($display === "table-footer-group") { + $this->_footers[] = $child; + } + } + } + + if ($anon_row && $table_row instanceof DOMNode) { + // Add the row to the table + $this->_frame->append_child($table_row); + $table_row->normalise(); + $this->_cellmap->add_row(); + } + + foreach ($erroneous_frames as $frame) { + $this->move_after($frame); + } + } + + //........................................................................ + + /** + * Moves the specified frame and it's corresponding node outside of + * the table. + * + * @param Frame $frame the frame to move + */ + public function move_after(Frame $frame) + { + $this->get_parent()->insert_child_after($frame, $this); + } +} \ No newline at end of file diff --git a/library/vendor/dompdf/src/FrameDecorator/TableCell.php b/library/vendor/dompdf/src/FrameDecorator/TableCell.php new file mode 100644 index 000000000..b8259d72f --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/TableCell.php @@ -0,0 +1,117 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; + +/** + * Decorates table cells for layout + * + * @package dompdf + */ +class TableCell extends BlockFrameDecorator +{ + + protected $_resolved_borders; + protected $_content_height; + + //........................................................................ + + function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + $this->_resolved_borders = array(); + $this->_content_height = 0; + } + + //........................................................................ + + function reset() + { + parent::reset(); + $this->_resolved_borders = array(); + $this->_content_height = 0; + $this->_frame->reset(); + } + + function get_content_height() + { + return $this->_content_height; + } + + function set_content_height($height) + { + $this->_content_height = $height; + } + + function set_cell_height($height) + { + $style = $this->get_style(); + $v_space = $style->length_in_pt(array($style->margin_top, + $style->padding_top, + $style->border_top_width, + $style->border_bottom_width, + $style->padding_bottom, + $style->margin_bottom), + $style->width); + + $new_height = $height - $v_space; + $style->height = $new_height; + + if ($new_height > $this->_content_height) { + $y_offset = 0; + + // Adjust our vertical alignment + switch ($style->vertical_align) { + default: + case "baseline": + // FIXME: this isn't right + + case "top": + // Don't need to do anything + return; + + case "middle": + $y_offset = ($new_height - $this->_content_height) / 2; + break; + + case "bottom": + $y_offset = $new_height - $this->_content_height; + break; + } + + if ($y_offset) { + // Move our children + foreach ($this->get_line_boxes() as $line) { + foreach ($line->get_frames() as $frame) + $frame->move(0, $y_offset); + } + } + } + + } + + function set_resolved_border($side, $border_spec) + { + $this->_resolved_borders[$side] = $border_spec; + } + + //........................................................................ + + function get_resolved_border($side) + { + return $this->_resolved_borders[$side]; + } + + function get_resolved_borders() + { + return $this->_resolved_borders; + } +} diff --git a/library/vendor/dompdf/src/FrameDecorator/TableRow.php b/library/vendor/dompdf/src/FrameDecorator/TableRow.php new file mode 100644 index 000000000..7f33082a2 --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/TableRow.php @@ -0,0 +1,52 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; +use Dompdf\FrameDecorator\Table as TableFrameDecorator; + +/** + * Decorates Frames for table row layout + * + * @package dompdf + */ +class TableRow extends AbstractFrameDecorator +{ + function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + } + + //........................................................................ + + /** + * Remove all non table-cell frames from this row and move them after + * the table. + */ + function normalise() + { + + // Find our table parent + $p = TableFrameDecorator::find_parent_table($this); + + $erroneous_frames = array(); + foreach ($this->get_children() as $child) { + $display = $child->get_style()->display; + + if ($display !== "table-cell") + $erroneous_frames[] = $child; + } + + // dump the extra nodes after the table. + foreach ($erroneous_frames as $frame) + $p->move_after($frame); + } + + +} diff --git a/library/vendor/dompdf/src/FrameDecorator/TableRowGroup.php b/library/vendor/dompdf/src/FrameDecorator/TableRowGroup.php new file mode 100644 index 000000000..0f2bb7967 --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/TableRowGroup.php @@ -0,0 +1,72 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; + +/** + * Table row group decorator + * + * Overrides split() method for tbody, thead & tfoot elements + * + * @package dompdf + */ +class TableRowGroup extends AbstractFrameDecorator +{ + + /** + * Class constructor + * + * @param Frame $frame Frame to decorate + * @param Dompdf $dompdf Current dompdf instance + */ + function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + } + + /** + * Override split() to remove all child rows and this element from the cellmap + * + * @param Frame $child + * @param bool $force_pagebreak + * + * @return void + */ + function split(Frame $child = null, $force_pagebreak = false) + { + + if (is_null($child)) { + parent::split(); + return; + } + + // Remove child & all subsequent rows from the cellmap + $cellmap = $this->get_parent()->get_cellmap(); + $iter = $child; + + while ($iter) { + $cellmap->remove_row($iter); + $iter = $iter->get_next_sibling(); + } + + // If we are splitting at the first child remove the + // table-row-group from the cellmap as well + if ($child === $this->get_first_child()) { + $cellmap->remove_row_group($this); + parent::split(); + return; + } + + $cellmap->update_row_group($this, $child->get_prev_sibling()); + parent::split($child); + + } +} + diff --git a/library/vendor/dompdf/src/FrameDecorator/Text.php b/library/vendor/dompdf/src/FrameDecorator/Text.php new file mode 100644 index 000000000..7a78879cb --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/Text.php @@ -0,0 +1,182 @@ + + * @author Brian Sweeney + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; +use Dompdf\Exception; +use DOMText; +use Dompdf\FontMetrics; + +/** + * Decorates Frame objects for text layout + * + * @access private + * @package dompdf + */ +class Text extends AbstractFrameDecorator +{ + + // protected members + protected $_text_spacing; + + function __construct(Frame $frame, Dompdf $dompdf) + { + if (!$frame->is_text_node()) { + throw new Exception("Text_Decorator can only be applied to #text nodes."); + } + + parent::__construct($frame, $dompdf); + $this->_text_spacing = null; + } + + //........................................................................ + + function reset() + { + parent::reset(); + $this->_text_spacing = null; + } + + //........................................................................ + + // Accessor methods + function get_text_spacing() + { + return $this->_text_spacing; + } + + function get_text() + { + // FIXME: this should be in a child class (and is incorrect) +// if ( $this->_frame->get_style()->content !== "normal" ) { +// $this->_frame->get_node()->data = $this->_frame->get_style()->content; +// $this->_frame->get_style()->content = "normal"; +// } + +// Helpers::pre_r("---"); +// $style = $this->_frame->get_style(); +// var_dump($text = $this->_frame->get_node()->data); +// var_dump($asc = utf8_decode($text)); +// for ($i = 0; $i < strlen($asc); $i++) +// Helpers::pre_r("$i: " . $asc[$i] . " - " . ord($asc[$i])); +// Helpers::pre_r("width: " . $this->_dompdf->getFontMetrics()->getTextWidth($text, $style->font_family, $style->font_size)); + + return $this->_frame->get_node()->data; + } + + //........................................................................ + + // Vertical margins & padding do not apply to text frames + + // http://www.w3.org/TR/CSS21/visudet.html#inline-non-replaced: + // + // The vertical padding, border and margin of an inline, non-replaced box + // start at the top and bottom of the content area, not the + // 'line-height'. But only the 'line-height' is used to calculate the + // height of the line box. + function get_margin_height() + { + // This function is called in add_frame_to_line() and is used to + // determine the line height, so we actually want to return the + // 'line-height' property, not the actual margin box + $style = $this->get_parent()->get_style(); + $font = $style->font_family; + $size = $style->font_size; + + /* + Helpers::pre_r('-----'); + Helpers::pre_r($style->line_height); + Helpers::pre_r($style->font_size); + Helpers::pre_r($this->_dompdf->getFontMetrics()->getFontHeight($font, $size)); + Helpers::pre_r(($style->line_height / $size) * $this->_dompdf->getFontMetrics()->getFontHeight($font, $size)); + */ + + return ($style->line_height / ($size > 0 ? $size : 1)) * $this->_dompdf->getFontMetrics()->getFontHeight($font, $size); + + } + + function get_padding_box() + { + $pb = $this->_frame->get_padding_box(); + $pb[3] = $pb["h"] = $this->_frame->get_style()->height; + + return $pb; + } + //........................................................................ + + // Set method + function set_text_spacing($spacing) + { + $style = $this->_frame->get_style(); + + $this->_text_spacing = $spacing; + $char_spacing = $style->length_in_pt($style->letter_spacing); + + // Re-adjust our width to account for the change in spacing + $style->width = $this->_dompdf->getFontMetrics()->getTextWidth($this->get_text(), $style->font_family, $style->font_size, $spacing, $char_spacing); + } + + //........................................................................ + + // Recalculate the text width + function recalculate_width() + { + $style = $this->get_style(); + $text = $this->get_text(); + $size = $style->font_size; + $font = $style->font_family; + $word_spacing = $style->length_in_pt($style->word_spacing); + $char_spacing = $style->length_in_pt($style->letter_spacing); + + return $style->width = $this->_dompdf->getFontMetrics()->getTextWidth($text, $font, $size, $word_spacing, $char_spacing); + } + + //........................................................................ + + // Text manipulation methods + + // split the text in this frame at the offset specified. The remaining + // text is added a sibling frame following this one and is returned. + function split_text($offset) + { + if ($offset == 0) { + return null; + } + + $split = $this->_frame->get_node()->splitText($offset); + + $deco = $this->copy($split); + + $p = $this->get_parent(); + $p->insert_child_after($deco, $this, false); + + if ($p instanceof Inline) { + $p->split($deco); + } + + return $deco; + } + + //........................................................................ + + function delete_text($offset, $count) + { + $this->_frame->get_node()->deleteData($offset, $count); + } + + //........................................................................ + + function set_text($text) + { + $this->_frame->get_node()->data = $text; + } + +} diff --git a/library/vendor/dompdf/src/FrameReflower/AbstractFrameReflower.php b/library/vendor/dompdf/src/FrameReflower/AbstractFrameReflower.php new file mode 100644 index 000000000..281ae44e4 --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/AbstractFrameReflower.php @@ -0,0 +1,460 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\Adapter\CPDF; +use Dompdf\Css\Style; +use Dompdf\Dompdf; +use Dompdf\Helpers; +use Dompdf\Frame; +use Dompdf\FrameDecorator\Block; +use Dompdf\Frame\Factory; + +/** + * Base reflower class + * + * Reflower objects are responsible for determining the width and height of + * individual frames. They also create line and page breaks as necessary. + * + * @package dompdf + */ +abstract class AbstractFrameReflower +{ + + /** + * Frame for this reflower + * + * @var Frame + */ + protected $_frame; + + /** + * Cached min/max size + * + * @var array + */ + protected $_min_max_cache; + + function __construct(Frame $frame) + { + $this->_frame = $frame; + $this->_min_max_cache = null; + } + + function dispose() + { + } + + /** + * @return Dompdf + */ + function get_dompdf() + { + return $this->_frame->get_dompdf(); + } + + /** + * Collapse frames margins + * http://www.w3.org/TR/CSS2/box.html#collapsing-margins + */ + protected function _collapse_margins() + { + $frame = $this->_frame; + $cb = $frame->get_containing_block(); + $style = $frame->get_style(); + + if (!$frame->is_in_flow()) { + return; + } + + $t = $style->length_in_pt($style->margin_top, $cb["h"]); + $b = $style->length_in_pt($style->margin_bottom, $cb["h"]); + + // Handle 'auto' values + if ($t === "auto") { + $style->margin_top = "0pt"; + $t = 0; + } + + if ($b === "auto") { + $style->margin_bottom = "0pt"; + $b = 0; + } + + // Collapse vertical margins: + $n = $frame->get_next_sibling(); + if ($n && !$n->is_block()) { + while ($n = $n->get_next_sibling()) { + if ($n->is_block()) { + break; + } + + if (!$n->get_first_child()) { + $n = null; + break; + } + } + } + + if ($n) { + $n_style = $n->get_style(); + $b = max($b, $n_style->length_in_pt($n_style->margin_top, $cb["h"])); + $n_style->margin_top = "0pt"; + $style->margin_bottom = $b . "pt"; + } + + // Collapse our first child's margin + /*$f = $this->_frame->get_first_child(); + if ( $f && !$f->is_block() ) { + while ( $f = $f->get_next_sibling() ) { + if ( $f->is_block() ) { + break; + } + + if ( !$f->get_first_child() ) { + $f = null; + break; + } + } + } + + // Margin are collapsed only between block elements + if ( $f ) { + $f_style = $f->get_style(); + $t = max($t, $f_style->length_in_pt($f_style->margin_top, $cb["h"])); + $style->margin_top = $t."pt"; + $f_style->margin_bottom = "0pt"; + }*/ + } + + //........................................................................ + + abstract function reflow(Block $block = null); + + //........................................................................ + + // Required for table layout: Returns an array(0 => min, 1 => max, "min" + // => min, "max" => max) of the minimum and maximum widths of this frame. + // This provides a basic implementation. Child classes should override + // this if necessary. + function get_min_max_width() + { + if (!is_null($this->_min_max_cache)) { + return $this->_min_max_cache; + } + + $style = $this->_frame->get_style(); + + // Account for margins & padding + $dims = array($style->padding_left, + $style->padding_right, + $style->border_left_width, + $style->border_right_width, + $style->margin_left, + $style->margin_right); + + $cb_w = $this->_frame->get_containing_block("w"); + $delta = $style->length_in_pt($dims, $cb_w); + + // Handle degenerate case + if (!$this->_frame->get_first_child()) { + return $this->_min_max_cache = array( + $delta, $delta, + "min" => $delta, + "max" => $delta, + ); + } + + $low = array(); + $high = array(); + + for ($iter = $this->_frame->get_children()->getIterator(); + $iter->valid(); + $iter->next()) { + + $inline_min = 0; + $inline_max = 0; + + // Add all adjacent inline widths together to calculate max width + while ($iter->valid() && in_array($iter->current()->get_style()->display, Style::$INLINE_TYPES)) { + + $child = $iter->current(); + + $minmax = $child->get_min_max_width(); + + if (in_array($iter->current()->get_style()->white_space, array("pre", "nowrap"))) { + $inline_min += $minmax["min"]; + } else { + $low[] = $minmax["min"]; + } + + $inline_max += $minmax["max"]; + $iter->next(); + + } + + if ($inline_max > 0) $high[] = $inline_max; + if ($inline_min > 0) $low[] = $inline_min; + + if ($iter->valid()) { + list($low[], $high[]) = $iter->current()->get_min_max_width(); + continue; + } + + } + $min = count($low) ? max($low) : 0; + $max = count($high) ? max($high) : 0; + + // Use specified width if it is greater than the minimum defined by the + // content. If the width is a percentage ignore it for now. + $width = $style->width; + if ($width !== "auto" && !Helpers::is_percent($width)) { + $width = $style->length_in_pt($width, $cb_w); + if ($min < $width) $min = $width; + if ($max < $width) $max = $width; + } + + $min += $delta; + $max += $delta; + return $this->_min_max_cache = array($min, $max, "min" => $min, "max" => $max); + } + + /** + * Parses a CSS string containing quotes and escaped hex characters + * + * @param $string string The CSS string to parse + * @param $single_trim + * @return string + */ + protected function _parse_string($string, $single_trim = false) + { + if ($single_trim) { + $string = preg_replace('/^[\"\']/', "", $string); + $string = preg_replace('/[\"\']$/', "", $string); + } else { + $string = trim($string, "'\""); + } + + $string = str_replace(array("\\\n", '\\"', "\\'"), + array("", '"', "'"), $string); + + // Convert escaped hex characters into ascii characters (e.g. \A => newline) + $string = preg_replace_callback("/\\\\([0-9a-fA-F]{0,6})/", + function ($matches) { return \Dompdf\Helpers::unichr(hexdec($matches[1])); }, + $string); + return $string; + } + + /** + * Parses a CSS "quotes" property + * + * @return array|null An array of pairs of quotes + */ + protected function _parse_quotes() + { + + // Matches quote types + $re = '/(\'[^\']*\')|(\"[^\"]*\")/'; + + $quotes = $this->_frame->get_style()->quotes; + + // split on spaces, except within quotes + if (!preg_match_all($re, "$quotes", $matches, PREG_SET_ORDER)) { + return null; + } + + $quotes_array = array(); + foreach ($matches as $_quote) { + $quotes_array[] = $this->_parse_string($_quote[0], true); + } + + if (empty($quotes_array)) { + $quotes_array = array('"', '"'); + } + + return array_chunk($quotes_array, 2); + } + + /** + * Parses the CSS "content" property + * + * @return string|null The resulting string + */ + protected function _parse_content() + { + + // Matches generated content + $re = "/\n" . + "\s(counters?\\([^)]*\\))|\n" . + "\A(counters?\\([^)]*\\))|\n" . + "\s([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?_frame->get_style()->content; + + $quotes = $this->_parse_quotes(); + + // split on spaces, except within quotes + if (!preg_match_all($re, $content, $matches, PREG_SET_ORDER)) { + return null; + } + + $text = ""; + + foreach ($matches as $match) { + + if (isset($match[2]) && $match[2] !== "") { + $match[1] = $match[2]; + } + + if (isset($match[6]) && $match[6] !== "") { + $match[4] = $match[6]; + } + + if (isset($match[8]) && $match[8] !== "") { + $match[7] = $match[8]; + } + + if (isset($match[1]) && $match[1] !== "") { + + // counters?(...) + $match[1] = mb_strtolower(trim($match[1])); + + // Handle counter() references: + // http://www.w3.org/TR/CSS21/generate.html#content + + $i = mb_strpos($match[1], ")"); + if ($i === false) { + continue; + } + + preg_match('/(counters?)(^\()*?\(\s*([^\s,]+)\s*(,\s*["\']?([^"\'\)]+)["\']?\s*(,\s*([^\s)]+)\s*)?)?\)/i', $match[1], $args); + $counter_id = $args[3]; + if (strtolower($args[1]) == 'counter') { + // counter(name [,style]) + if (isset($args[5])) { + $type = trim($args[5]); + } else { + $type = null; + } + $p = $this->_frame->lookup_counter_frame($counter_id); + + $text .= $p->counter_value($counter_id, $type); + + } else if (strtolower($args[1]) == 'counters') { + // counters(name, string [,style]) + if (isset($args[5])) { + $string = $this->_parse_string($args[5]); + } else { + $string = ""; + } + + if (isset($args[7])) { + $type = trim($args[7]); + } else { + $type = null; + } + + $p = $this->_frame->lookup_counter_frame($counter_id); + $tmp = array(); + while ($p) { + // We only want to use the counter values when they actually increment the counter + if (array_key_exists($counter_id, $p->_counters)) { + array_unshift($tmp, $p->counter_value($counter_id, $type)); + } + $p = $p->lookup_counter_frame($counter_id); + + } + $text .= implode($string, $tmp); + + } else { + // countertops? + continue; + } + + } else if (isset($match[4]) && $match[4] !== "") { + // String match + $text .= $this->_parse_string($match[4]); + } else if (isset($match[7]) && $match[7] !== "") { + // Directive match + + if ($match[7] === "open-quote") { + // FIXME: do something here + $text .= $quotes[0][0]; + } else if ($match[7] === "close-quote") { + // FIXME: do something else here + $text .= $quotes[0][1]; + } else if ($match[7] === "no-open-quote") { + // FIXME: + } else if ($match[7] === "no-close-quote") { + // FIXME: + } else if (mb_strpos($match[7], "attr(") === 0) { + + $i = mb_strpos($match[7], ")"); + if ($i === false) { + continue; + } + + $attr = mb_substr($match[7], 5, $i - 5); + if ($attr == "") { + continue; + } + + $text .= $this->_frame->get_parent()->get_node()->getAttribute($attr); + } else { + continue; + } + } + } + + return $text; + } + + /** + * Sets the generated content of a generated frame + */ + protected function _set_content() + { + $frame = $this->_frame; + $style = $frame->get_style(); + + // if the element was pushed to a new page use the saved counter value, otherwise use the CSS reset value + if ($style->counter_reset && ($reset = $style->counter_reset) !== "none") { + $vars = preg_split('/\s+/', trim($reset), 2); + $frame->reset_counter($vars[0], (isset($frame->_counters['__' . $vars[0]]) ? $frame->_counters['__' . $vars[0]] : (isset($vars[1]) ? $vars[1] : 0))); + } + + if ($style->counter_increment && ($increment = $style->counter_increment) !== "none") { + $frame->increment_counters($increment); + } + + if ($style->content && !$frame->get_first_child() && $frame->get_node()->nodeName === "dompdf_generated") { + $content = $this->_parse_content(); + // add generated content to the font subset + // FIXME: This is currently too late because the font subset has already been generated. + // See notes in issue #750. + if ($frame->get_dompdf()->get_option("enable_font_subsetting") && $frame->get_dompdf()->get_canvas() instanceof CPDF) { + $frame->get_dompdf()->get_canvas()->register_string_subset($style->font_family, $content); + } + + $node = $frame->get_node()->ownerDocument->createTextNode($content); + + $new_style = $style->get_stylesheet()->create_style(); + $new_style->inherit($style); + + $new_frame = new Frame($node); + $new_frame->set_style($new_style); + + Factory::decorate_frame($new_frame, $frame->get_dompdf(), $frame->get_root()); + $frame->append_child($new_frame); + } + } +} diff --git a/library/vendor/dompdf/src/FrameReflower/Block.php b/library/vendor/dompdf/src/FrameReflower/Block.php new file mode 100644 index 000000000..db5da39c9 --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/Block.php @@ -0,0 +1,793 @@ + + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\FontMetrics; +use Dompdf\Frame; +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\Text as TextFrameDecorator; +use Dompdf\Exception; + +/** + * Reflows block frames + * + * @package dompdf + */ +class Block extends AbstractFrameReflower +{ + // Minimum line width to justify, as fraction of available width + const MIN_JUSTIFY_WIDTH = 0.80; + + /** + * @var BlockFrameDecorator + */ + protected $_frame; + + function __construct(BlockFrameDecorator $frame) + { + parent::__construct($frame); + } + + /** + * Calculate the ideal used value for the width property as per: + * http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins + * + * @param float $width + * + * @return array + */ + protected function _calculate_width($width) + { + $frame = $this->_frame; + $style = $frame->get_style(); + $w = $frame->get_containing_block("w"); + + if ($style->position === "fixed") { + $w = $frame->get_parent()->get_containing_block("w"); + } + + $rm = $style->length_in_pt($style->margin_right, $w); + $lm = $style->length_in_pt($style->margin_left, $w); + + $left = $style->length_in_pt($style->left, $w); + $right = $style->length_in_pt($style->right, $w); + + // Handle 'auto' values + $dims = array($style->border_left_width, + $style->border_right_width, + $style->padding_left, + $style->padding_right, + $width !== "auto" ? $width : 0, + $rm !== "auto" ? $rm : 0, + $lm !== "auto" ? $lm : 0); + + // absolutely positioned boxes take the 'left' and 'right' properties into account + if ($frame->is_absolute()) { + $absolute = true; + $dims[] = $left !== "auto" ? $left : 0; + $dims[] = $right !== "auto" ? $right : 0; + } else { + $absolute = false; + } + + $sum = $style->length_in_pt($dims, $w); + + // Compare to the containing block + $diff = $w - $sum; + + if ($diff > 0) { + + if ($absolute) { + + // resolve auto properties: see + // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width + + if ($width === "auto" && $left === "auto" && $right === "auto") { + + if ($lm === "auto") $lm = 0; + if ($rm === "auto") $rm = 0; + + // Technically, the width should be "shrink-to-fit" i.e. based on the + // preferred width of the content... a little too costly here as a + // special case. Just get the width to take up the slack: + $left = 0; + $right = 0; + $width = $diff; + } else if ($width === "auto") { + + if ($lm === "auto") $lm = 0; + if ($rm === "auto") $rm = 0; + if ($left === "auto") $left = 0; + if ($right === "auto") $right = 0; + + $width = $diff; + } else if ($left === "auto") { + + if ($lm === "auto") $lm = 0; + if ($rm === "auto") $rm = 0; + if ($right === "auto") $right = 0; + + $left = $diff; + } else if ($right === "auto") { + + if ($lm === "auto") $lm = 0; + if ($rm === "auto") $rm = 0; + + $right = $diff; + } + + } else { + + // Find auto properties and get them to take up the slack + if ($width === "auto") { + $width = $diff; + } else if ($lm === "auto" && $rm === "auto") { + $lm = $rm = round($diff / 2); + } else if ($lm === "auto") { + $lm = $diff; + } else if ($rm === "auto") { + $rm = $diff; + } + } + + } else if ($diff < 0) { + + // We are over constrained--set margin-right to the difference + $rm = $diff; + + } + + return array( + "width" => $width, + "margin_left" => $lm, + "margin_right" => $rm, + "left" => $left, + "right" => $right, + ); + } + + /** + * Call the above function, but resolve max/min widths + * + * @throws Exception + * @return array + */ + protected function _calculate_restricted_width() + { + $frame = $this->_frame; + $style = $frame->get_style(); + $cb = $frame->get_containing_block(); + + if ($style->position === "fixed") { + $cb = $frame->get_root()->get_containing_block(); + } + + //if ( $style->position === "absolute" ) + // $cb = $frame->find_positionned_parent()->get_containing_block(); + + if (!isset($cb["w"])) { + throw new Exception("Box property calculation requires containing block width"); + } + + // Treat width 100% as auto + if ($style->width === "100%") { + $width = "auto"; + } else { + $width = $style->length_in_pt($style->width, $cb["w"]); + } + + extract($this->_calculate_width($width)); + + // Handle min/max width + $min_width = $style->length_in_pt($style->min_width, $cb["w"]); + $max_width = $style->length_in_pt($style->max_width, $cb["w"]); + + if ($max_width !== "none" && $min_width > $max_width) { + list($max_width, $min_width) = array($min_width, $max_width); + } + + if ($max_width !== "none" && $width > $max_width) { + extract($this->_calculate_width($max_width)); + } + + if ($width < $min_width) { + extract($this->_calculate_width($min_width)); + } + + return array($width, $margin_left, $margin_right, $left, $right); + } + + /** + * Determine the unrestricted height of content within the block + * not by adding each line's height, but by getting the last line's position. + * This because lines could have been pushed lower by a clearing element. + * + * @return float + */ + protected function _calculate_content_height() + { + $lines = $this->_frame->get_line_boxes(); + $height = 0; + + foreach ($lines as $line) { + $height += $line->h; + } + + /* + $first_line = reset($lines); + $last_line = end($lines); + $height2 = $last_line->y + $last_line->h - $first_line->y; + */ + + return $height; + } + + /** + * Determine the frame's restricted height + * + * @return array + */ + protected function _calculate_restricted_height() + { + $frame = $this->_frame; + $style = $frame->get_style(); + $content_height = $this->_calculate_content_height(); + $cb = $frame->get_containing_block(); + + $height = $style->length_in_pt($style->height, $cb["h"]); + + $top = $style->length_in_pt($style->top, $cb["h"]); + $bottom = $style->length_in_pt($style->bottom, $cb["h"]); + + $margin_top = $style->length_in_pt($style->margin_top, $cb["h"]); + $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]); + + if ($frame->is_absolute()) { + + // see http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height + + $dims = array($top !== "auto" ? $top : 0, + $style->margin_top !== "auto" ? $style->margin_top : 0, + $style->padding_top, + $style->border_top_width, + $height !== "auto" ? $height : 0, + $style->border_bottom_width, + $style->padding_bottom, + $style->margin_bottom !== "auto" ? $style->margin_bottom : 0, + $bottom !== "auto" ? $bottom : 0); + + $sum = $style->length_in_pt($dims, $cb["h"]); + + $diff = $cb["h"] - $sum; + + if ($diff > 0) { + + if ($height === "auto" && $top === "auto" && $bottom === "auto") { + + if ($margin_top === "auto") $margin_top = 0; + if ($margin_bottom === "auto") $margin_bottom = 0; + + $height = $diff; + } else if ($height === "auto" && $top === "auto") { + + if ($margin_top === "auto") $margin_top = 0; + if ($margin_bottom === "auto") $margin_bottom = 0; + + $height = $content_height; + $top = $diff - $content_height; + } else if ($height === "auto" && $bottom === "auto") { + + if ($margin_top === "auto") $margin_top = 0; + if ($margin_bottom === "auto") $margin_bottom = 0; + + $height = $content_height; + $bottom = $diff - $content_height; + } else if ($top === "auto" && $bottom === "auto") { + + if ($margin_top === "auto") $margin_top = 0; + if ($margin_bottom === "auto") $margin_bottom = 0; + + $bottom = $diff; + } else if ($top === "auto") { + + if ($margin_top === "auto") $margin_top = 0; + if ($margin_bottom === "auto") $margin_bottom = 0; + + $top = $diff; + } else if ($height === "auto") { + + if ($margin_top === "auto") $margin_top = 0; + if ($margin_bottom === "auto") $margin_bottom = 0; + + $height = $diff; + } else if ($bottom === "auto") { + + if ($margin_top === "auto") $margin_top = 0; + if ($margin_bottom === "auto") $margin_bottom = 0; + + $bottom = $diff; + } else { + + if ($style->overflow === "visible") { + // set all autos to zero + if ($margin_top === "auto") $margin_top = 0; + if ($margin_bottom === "auto") $margin_bottom = 0; + if ($top === "auto") $top = 0; + if ($bottom === "auto") $bottom = 0; + if ($height === "auto") $height = $content_height; + } + + // FIXME: overflow hidden + } + + } + + } else { + + // Expand the height if overflow is visible + if ($height === "auto" && $content_height > $height /* && $style->overflow === "visible" */) { + $height = $content_height; + } + + // FIXME: this should probably be moved to a seperate function as per + // _calculate_restricted_width + + // Only handle min/max height if the height is independent of the frame's content + if (!($style->overflow === "visible" || + ($style->overflow === "hidden" && $height === "auto")) + ) { + + $min_height = $style->min_height; + $max_height = $style->max_height; + + if (isset($cb["h"])) { + $min_height = $style->length_in_pt($min_height, $cb["h"]); + $max_height = $style->length_in_pt($max_height, $cb["h"]); + + } else if (isset($cb["w"])) { + + if (mb_strpos($min_height, "%") !== false) { + $min_height = 0; + } else { + $min_height = $style->length_in_pt($min_height, $cb["w"]); + } + + if (mb_strpos($max_height, "%") !== false) { + $max_height = "none"; + } else { + $max_height = $style->length_in_pt($max_height, $cb["w"]); + } + } + + if ($max_height !== "none" && $min_height > $max_height) { + // Swap 'em + list($max_height, $min_height) = array($min_height, $max_height); + } + + if ($max_height !== "none" && $height > $max_height) { + $height = $max_height; + } + + if ($height < $min_height) { + $height = $min_height; + } + } + + } + + return array($height, $margin_top, $margin_bottom, $top, $bottom); + + } + + /** + * Adjust the justification of each of our lines. + * http://www.w3.org/TR/CSS21/text.html#propdef-text-align + */ + protected function _text_align() + { + $style = $this->_frame->get_style(); + $w = $this->_frame->get_containing_block("w"); + $width = $style->length_in_pt($style->width, $w); + + switch ($style->text_align) { + default: + case "left": + foreach ($this->_frame->get_line_boxes() as $line) { + if (!$line->left) { + continue; + } + + foreach ($line->get_frames() as $frame) { + if ($frame instanceof BlockFrameDecorator) { + continue; + } + $frame->set_position($frame->get_position("x") + $line->left); + } + } + return; + + case "right": + foreach ($this->_frame->get_line_boxes() as $line) { + // Move each child over by $dx + $dx = $width - $line->w - $line->right; + + foreach ($line->get_frames() as $frame) { + // Block frames are not aligned by text-align + if ($frame instanceof BlockFrameDecorator) { + continue; + } + + $frame->set_position($frame->get_position("x") + $dx); + } + } + break; + + + case "justify": + // We justify all lines except the last one + $lines = $this->_frame->get_line_boxes(); // needs to be a variable (strict standards) + array_pop($lines); + + foreach ($lines as $i => $line) { + if ($line->br) { + unset($lines[$i]); + } + } + + // One space character's width. Will be used to get a more accurate spacing + $space_width = $this->get_dompdf()->getFontMetrics()->getTextWidth(" ", $style->font_family, $style->font_size); + + foreach ($lines as $line) { + if ($line->left) { + foreach ($line->get_frames() as $frame) { + if (!$frame instanceof TextFrameDecorator) { + continue; + } + + $frame->set_position($frame->get_position("x") + $line->left); + } + } + + // Only set the spacing if the line is long enough. This is really + // just an aesthetic choice ;) + //if ( $line["left"] + $line["w"] + $line["right"] > self::MIN_JUSTIFY_WIDTH * $width ) { + + // Set the spacing for each child + if ($line->wc > 1) { + $spacing = ($width - ($line->left + $line->w + $line->right) + $space_width) / ($line->wc - 1); + } else { + $spacing = 0; + } + + $dx = 0; + foreach ($line->get_frames() as $frame) { + if (!$frame instanceof TextFrameDecorator) { + continue; + } + + $text = $frame->get_text(); + $spaces = mb_substr_count($text, " "); + + $char_spacing = $style->length_in_pt($style->letter_spacing); + $_spacing = $spacing + $char_spacing; + + $frame->set_position($frame->get_position("x") + $dx); + $frame->set_text_spacing($_spacing); + + $dx += $spaces * $_spacing; + } + + // The line (should) now occupy the entire width + $line->w = $width; + + //} + } + break; + + case "center": + case "centre": + foreach ($this->_frame->get_line_boxes() as $line) { + // Centre each line by moving each frame in the line by: + $dx = ($width + $line->left - $line->w - $line->right) / 2; + + foreach ($line->get_frames() as $frame) { + // Block frames are not aligned by text-align + if ($frame instanceof BlockFrameDecorator) { + continue; + } + + $frame->set_position($frame->get_position("x") + $dx); + } + } + break; + } + } + + /** + * Align inline children vertically. + * Aligns each child vertically after each line is reflowed + */ + function vertical_align() + { + + $canvas = null; + + foreach ($this->_frame->get_line_boxes() as $line) { + + $height = $line->h; + + foreach ($line->get_frames() as $frame) { + $style = $frame->get_style(); + + if ($style->display !== "inline") { + continue; + } + + $align = $frame->get_parent()->get_style()->vertical_align; + + if (!isset($canvas)) { + $canvas = $frame->get_root()->get_dompdf()->get_canvas(); + } + + $baseline = $canvas->get_font_baseline($style->font_family, $style->font_size); + $y_offset = 0; + + switch ($align) { + case "baseline": + $y_offset = $height * 0.8 - $baseline; // The 0.8 ratio is arbitrary until we find it's meaning + break; + + case "middle": + $y_offset = ($height * 0.8 - $baseline) / 2; + break; + + case "sub": + $y_offset = 0.3 * $height; + break; + + case "super": + $y_offset = -0.2 * $height; + break; + + case "text-top": + case "top": // Not strictly accurate, but good enough for now + break; + + case "text-bottom": + case "bottom": + $y_offset = $height * 0.8 - $baseline; + break; + } + + if ($y_offset) { + $frame->move(0, $y_offset); + } + } + } + } + + /** + * @param Frame $child + */ + function process_clear(Frame $child) + { + $child_style = $child->get_style(); + $root = $this->_frame->get_root(); + + // Handle "clear" + if ($child_style->clear !== "none") { + $lowest_y = $root->get_lowest_float_offset($child); + + // If a float is still applying, we handle it + if ($lowest_y) { + if ($child->is_in_flow()) { + $line_box = $this->_frame->get_current_line_box(); + $line_box->y = $lowest_y + $child->get_margin_height(); + $line_box->left = 0; + $line_box->right = 0; + } + + $child->move(0, $lowest_y - $child->get_position("y")); + } + } + } + + /** + * @param Frame $child + * @param float $cb_x + * @param float $cb_w + */ + function process_float(Frame $child, $cb_x, $cb_w) + { + $child_style = $child->get_style(); + $root = $this->_frame->get_root(); + + // Handle "float" + if ($child_style->float !== "none") { + $root->add_floating_frame($child); + + // Remove next frame's beginning whitespace + $next = $child->get_next_sibling(); + if ($next && $next instanceof TextFrameDecorator) { + $next->set_text(ltrim($next->get_text())); + } + + $line_box = $this->_frame->get_current_line_box(); + list($old_x, $old_y) = $child->get_position(); + + $float_x = $cb_x; + $float_y = $old_y; + $float_w = $child->get_margin_width(); + + if ($child_style->clear === "none") { + switch ($child_style->float) { + case "left": + $float_x += $line_box->left; + break; + case "right": + $float_x += ($cb_w - $line_box->right - $float_w); + break; + } + } else { + if ($child_style->float === "right") { + $float_x += ($cb_w - $float_w); + } + } + + if ($cb_w < $float_x + $float_w - $old_x) { + // TODO handle when floating elements don't fit + } + + $line_box->get_float_offsets(); + + if ($child->_float_next_line) { + $float_y += $line_box->h; + } + + $child->set_position($float_x, $float_y); + $child->move($float_x - $old_x, $float_y - $old_y, true); + } + } + + /** + * @param BlockFrameDecorator $block + */ + function reflow(BlockFrameDecorator $block = null) + { + + // Check if a page break is forced + $page = $this->_frame->get_root(); + $page->check_forced_page_break($this->_frame); + + // Bail if the page is full + if ($page->is_full()) { + return; + } + + // Generated content + $this->_set_content(); + + // Collapse margins if required + $this->_collapse_margins(); + + $style = $this->_frame->get_style(); + $cb = $this->_frame->get_containing_block(); + + if ($style->position === "fixed") { + $cb = $this->_frame->get_root()->get_containing_block(); + } + + // Determine the constraints imposed by this frame: calculate the width + // of the content area: + list($w, $left_margin, $right_margin, $left, $right) = $this->_calculate_restricted_width(); + + // Store the calculated properties + $style->width = $w . "pt"; + $style->margin_left = $left_margin . "pt"; + $style->margin_right = $right_margin . "pt"; + $style->left = $left . "pt"; + $style->right = $right . "pt"; + + // Update the position + $this->_frame->position(); + list($x, $y) = $this->_frame->get_position(); + + // Adjust the first line based on the text-indent property + $indent = $style->length_in_pt($style->text_indent, $cb["w"]); + $this->_frame->increase_line_width($indent); + + // Determine the content edge + $top = $style->length_in_pt(array($style->margin_top, + $style->padding_top, + $style->border_top_width), $cb["h"]); + + $bottom = $style->length_in_pt(array($style->border_bottom_width, + $style->margin_bottom, + $style->padding_bottom), $cb["h"]); + + $cb_x = $x + $left_margin + $style->length_in_pt(array($style->border_left_width, + $style->padding_left), $cb["w"]); + + $cb_y = $y + $top; + + $cb_h = ($cb["h"] + $cb["y"]) - $bottom - $cb_y; + + // Set the y position of the first line in this block + $line_box = $this->_frame->get_current_line_box(); + $line_box->y = $cb_y; + $line_box->get_float_offsets(); + + // Set the containing blocks and reflow each child + foreach ($this->_frame->get_children() as $child) { + + // Bail out if the page is full + if ($page->is_full()) { + break; + } + + $child->set_containing_block($cb_x, $cb_y, $w, $cb_h); + + $this->process_clear($child); + + $child->reflow($this->_frame); + + // Don't add the child to the line if a page break has occurred + if ($page->check_page_break($child)) { + break; + } + + $this->process_float($child, $cb_x, $w); + } + + // Determine our height + list($height, $margin_top, $margin_bottom, $top, $bottom) = $this->_calculate_restricted_height(); + $style->height = $height; + $style->margin_top = $margin_top; + $style->margin_bottom = $margin_bottom; + $style->top = $top; + $style->bottom = $bottom; + + $needs_reposition = ($style->position === "absolute" && ($style->right !== "auto" || $style->bottom !== "auto")); + + // Absolute positioning measurement + if ($needs_reposition) { + $orig_style = $this->_frame->get_original_style(); + if ($orig_style->width === "auto" && ($orig_style->left === "auto" || $orig_style->right === "auto")) { + $width = 0; + foreach ($this->_frame->get_line_boxes() as $line) { + $width = max($line->w, $width); + } + $style->width = $width; + } + + $style->left = $orig_style->left; + $style->right = $orig_style->right; + } + + $this->_text_align(); + $this->vertical_align(); + + // Absolute positioning + if ($needs_reposition) { + list($x, $y) = $this->_frame->get_position(); + $this->_frame->position(); + list($new_x, $new_y) = $this->_frame->get_position(); + $this->_frame->move($new_x - $x, $new_y - $y, true); + } + + if ($block && $this->_frame->is_in_flow()) { + $block->add_frame_to_line($this->_frame); + + // May be inline-block + if ($style->display === "block") { + $block->add_line(); + } + } + } +} diff --git a/library/vendor/dompdf/src/FrameReflower/Image.php b/library/vendor/dompdf/src/FrameReflower/Image.php new file mode 100644 index 000000000..c59814d5c --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/Image.php @@ -0,0 +1,195 @@ + + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\Helpers; +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\Image as ImageFrameDecorator; + +/** + * Image reflower class + * + * @package dompdf + */ +class Image extends AbstractFrameReflower +{ + + function __construct(ImageFrameDecorator $frame) + { + parent::__construct($frame); + } + + function reflow(BlockFrameDecorator $block = null) + { + $this->_frame->position(); + + //FLOAT + //$frame = $this->_frame; + //$page = $frame->get_root(); + + //if ($frame->get_style()->float !== "none" ) { + // $page->add_floating_frame($this); + //} + + // Set the frame's width + $this->get_min_max_width(); + + if ($block) { + $block->add_frame_to_line($this->_frame); + } + } + + function get_min_max_width() + { + if ($this->get_dompdf()->get_option("debugPng")) { + // Determine the image's size. Time consuming. Only when really needed? + list($img_width, $img_height) = Helpers::dompdf_getimagesize($this->_frame->get_image_url(), $this->get_dompdf()->getHttpContext()); + print "get_min_max_width() " . + $this->_frame->get_style()->width . ' ' . + $this->_frame->get_style()->height . ';' . + $this->_frame->get_parent()->get_style()->width . " " . + $this->_frame->get_parent()->get_style()->height . ";" . + $this->_frame->get_parent()->get_parent()->get_style()->width . ' ' . + $this->_frame->get_parent()->get_parent()->get_style()->height . ';' . + $img_width . ' ' . + $img_height . '|'; + } + + $style = $this->_frame->get_style(); + + $width_forced = true; + $height_forced = true; + + //own style auto or invalid value: use natural size in px + //own style value: ignore suffix text including unit, use given number as px + //own style %: walk up parent chain until found available space in pt; fill available space + // + //special ignored unit: e.g. 10ex: e treated as exponent; x ignored; 10e completely invalid ->like auto + + $width = ($style->width > 0 ? $style->width : 0); + if (Helpers::is_percent($width)) { + $t = 0.0; + for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) { + $f_style = $f->get_style(); + $t = $f_style->length_in_pt($f_style->width); + if ($t != 0) { + break; + } + } + $width = ((float)rtrim($width, "%") * $t) / 100; //maybe 0 + } elseif (!mb_strpos($width, 'pt')) { + // Don't set image original size if "%" branch was 0 or size not given. + // Otherwise aspect changed on %/auto combination for width/height + // Resample according to px per inch + // See also ListBulletImage::__construct + $width = $style->length_in_pt($width); + } + + $height = ($style->height > 0 ? $style->height : 0); + if (Helpers::is_percent($height)) { + $t = 0.0; + for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) { + $f_style = $f->get_style(); + $t = $f_style->length_in_pt($f_style->height); + if ($t != 0) { + break; + } + } + $height = ((float)rtrim($height, "%") * $t) / 100; //maybe 0 + } elseif (!mb_strpos($height, 'pt')) { + // Don't set image original size if "%" branch was 0 or size not given. + // Otherwise aspect changed on %/auto combination for width/height + // Resample according to px per inch + // See also ListBulletImage::__construct + $height = $style->length_in_pt($height); + } + + if ($width == 0 || $height == 0) { + // Determine the image's size. Time consuming. Only when really needed! + list($img_width, $img_height) = Helpers::dompdf_getimagesize($this->_frame->get_image_url(), $this->get_dompdf()->getHttpContext()); + + // don't treat 0 as error. Can be downscaled or can be catched elsewhere if image not readable. + // Resample according to px per inch + // See also ListBulletImage::__construct + if ($width == 0 && $height == 0) { + $dpi = $this->_frame->get_dompdf()->get_option("dpi"); + $width = (float)($img_width * 72) / $dpi; + $height = (float)($img_height * 72) / $dpi; + $width_forced = false; + $height_forced = false; + } elseif ($height == 0 && $width != 0) { + $height_forced = false; + $height = ($width / $img_width) * $img_height; //keep aspect ratio + } elseif ($width == 0 && $height != 0) { + $width_forced = false; + $width = ($height / $img_height) * $img_width; //keep aspect ratio + } + } + + // Handle min/max width/height + if ($style->min_width !== "none" || + $style->max_width !== "none" || + $style->min_height !== "none" || + $style->max_height !== "none" + ) { + + list( /*$x*/, /*$y*/, $w, $h) = $this->_frame->get_containing_block(); + + $min_width = $style->length_in_pt($style->min_width, $w); + $max_width = $style->length_in_pt($style->max_width, $w); + $min_height = $style->length_in_pt($style->min_height, $h); + $max_height = $style->length_in_pt($style->max_height, $h); + + if ($max_width !== "none" && $width > $max_width) { + if (!$height_forced) { + $height *= $max_width / $width; + } + + $width = $max_width; + } + + if ($min_width !== "none" && $width < $min_width) { + if (!$height_forced) { + $height *= $min_width / $width; + } + + $width = $min_width; + } + + if ($max_height !== "none" && $height > $max_height) { + if (!$width_forced) { + $width *= $max_height / $height; + } + + $height = $max_height; + } + + if ($min_height !== "none" && $height < $min_height) { + if (!$width_forced) { + $width *= $min_height / $height; + } + + $height = $min_height; + } + } + + if ($this->get_dompdf()->get_option("debugPng")) print $width . ' ' . $height . ';'; + + $style->width = $width . "pt"; + $style->height = $height . "pt"; + + $style->min_width = "none"; + $style->max_width = "none"; + $style->min_height = "none"; + $style->max_height = "none"; + + return array($width, $width, "min" => $width, "max" => $width); + + } +} diff --git a/library/vendor/dompdf/src/FrameReflower/Inline.php b/library/vendor/dompdf/src/FrameReflower/Inline.php new file mode 100644 index 000000000..1f05fa9ef --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/Inline.php @@ -0,0 +1,76 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\Frame; +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\Text as TextFrameDecorator; + +/** + * Reflows inline frames + * + * @package dompdf + */ +class Inline extends AbstractFrameReflower +{ + + function __construct(Frame $frame) + { + parent::__construct($frame); + } + + //........................................................................ + + function reflow(BlockFrameDecorator $block = null) + { + $frame = $this->_frame; + + // Check if a page break is forced + $page = $frame->get_root(); + $page->check_forced_page_break($frame); + + if ($page->is_full()) { + return; + } + + $style = $frame->get_style(); + + // Generated content + $this->_set_content(); + + $frame->position(); + + $cb = $frame->get_containing_block(); + + // Add our margin, padding & border to the first and last children + if (($f = $frame->get_first_child()) && $f instanceof TextFrameDecorator) { + $f_style = $f->get_style(); + $f_style->margin_left = $style->margin_left; + $f_style->padding_left = $style->padding_left; + $f_style->border_left = $style->border_left; + } + + if (($l = $frame->get_last_child()) && $l instanceof TextFrameDecorator) { + $l_style = $l->get_style(); + $l_style->margin_right = $style->margin_right; + $l_style->padding_right = $style->padding_right; + $l_style->border_right = $style->border_right; + } + + if ($block) { + $block->add_frame_to_line($this->_frame); + } + + // Set the containing blocks and reflow each child. The containing + // block is not changed by line boxes. + foreach ($frame->get_children() as $child) { + $child->set_containing_block($cb); + $child->reflow($block); + } + } +} diff --git a/library/vendor/dompdf/src/FrameReflower/ListBullet.php b/library/vendor/dompdf/src/FrameReflower/ListBullet.php new file mode 100644 index 000000000..553195db5 --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/ListBullet.php @@ -0,0 +1,41 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\AbstractFrameDecorator; + +/** + * Reflows list bullets + * + * @package dompdf + */ +class ListBullet extends AbstractFrameReflower +{ + + function __construct(AbstractFrameDecorator $frame) + { + parent::__construct($frame); + } + + //........................................................................ + + function reflow(BlockFrameDecorator $block = null) + { + $style = $this->_frame->get_style(); + + $style->width = $this->_frame->get_width(); + $this->_frame->position(); + + if ($style->list_style_position === "inside") { + $p = $this->_frame->find_block_parent(); + $p->add_frame_to_line($this->_frame); + } + + } +} diff --git a/library/vendor/dompdf/src/FrameReflower/NullFrameReflower.php b/library/vendor/dompdf/src/FrameReflower/NullFrameReflower.php new file mode 100644 index 000000000..1f9d2e2c0 --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/NullFrameReflower.php @@ -0,0 +1,32 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\FrameReflower; + +use Dompdf\Frame; +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; + +/** + * Dummy reflower + * + * @package dompdf + */ +class NullFrameReflower extends AbstractFrameReflower +{ + + function __construct(Frame $frame) + { + parent::__construct($frame); + } + + function reflow(BlockFrameDecorator $block = null) + { + return; + } + +} diff --git a/library/vendor/dompdf/src/FrameReflower/Page.php b/library/vendor/dompdf/src/FrameReflower/Page.php new file mode 100644 index 000000000..770eb6c83 --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/Page.php @@ -0,0 +1,199 @@ + + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\Frame; +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\Page as PageFrameDecorator; + +/** + * Reflows pages + * + * @package dompdf + */ +class Page extends AbstractFrameReflower +{ + + /** + * Cache of the callbacks array + * + * @var array + */ + private $_callbacks; + + /** + * Cache of the canvas + * + * @var \Dompdf\Canvas + */ + private $_canvas; + + function __construct(PageFrameDecorator $frame) + { + parent::__construct($frame); + } + + function apply_page_style(Frame $frame, $page_number) + { + $style = $frame->get_style(); + $page_styles = $style->get_stylesheet()->get_page_styles(); + + // http://www.w3.org/TR/CSS21/page.html#page-selectors + if (count($page_styles) > 1) { + $odd = $page_number % 2 == 1; + $first = $page_number == 1; + + $style = clone $page_styles["base"]; + + // FIXME RTL + if ($odd && isset($page_styles[":right"])) { + $style->merge($page_styles[":right"]); + } + + if ($odd && isset($page_styles[":odd"])) { + $style->merge($page_styles[":odd"]); + } + + // FIXME RTL + if (!$odd && isset($page_styles[":left"])) { + $style->merge($page_styles[":left"]); + } + + if (!$odd && isset($page_styles[":even"])) { + $style->merge($page_styles[":even"]); + } + + if ($first && isset($page_styles[":first"])) { + $style->merge($page_styles[":first"]); + } + + $frame->set_style($style); + } + } + + //........................................................................ + + /** + * Paged layout: + * http://www.w3.org/TR/CSS21/page.html + */ + function reflow(BlockFrameDecorator $block = null) + { + $fixed_children = array(); + $prev_child = null; + $child = $this->_frame->get_first_child(); + $current_page = 0; + + while ($child) { + $this->apply_page_style($this->_frame, $current_page + 1); + + $style = $this->_frame->get_style(); + + // Pages are only concerned with margins + $cb = $this->_frame->get_containing_block(); + $left = $style->length_in_pt($style->margin_left, $cb["w"]); + $right = $style->length_in_pt($style->margin_right, $cb["w"]); + $top = $style->length_in_pt($style->margin_top, $cb["h"]); + $bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]); + + $content_x = $cb["x"] + $left; + $content_y = $cb["y"] + $top; + $content_width = $cb["w"] - $left - $right; + $content_height = $cb["h"] - $top - $bottom; + + // Only if it's the first page, we save the nodes with a fixed position + if ($current_page == 0) { + $children = $child->get_children(); + foreach ($children as $onechild) { + if ($onechild->get_style()->position === "fixed") { + $fixed_children[] = $onechild->deep_copy(); + } + } + $fixed_children = array_reverse($fixed_children); + } + + $child->set_containing_block($content_x, $content_y, $content_width, $content_height); + + // Check for begin reflow callback + $this->_check_callbacks("begin_page_reflow", $child); + + //Insert a copy of each node which have a fixed position + if ($current_page >= 1) { + foreach ($fixed_children as $fixed_child) { + $child->insert_child_before($fixed_child->deep_copy(), $child->get_first_child()); + } + } + + $child->reflow(); + $next_child = $child->get_next_sibling(); + + // Check for begin render callback + $this->_check_callbacks("begin_page_render", $child); + + // Render the page + $this->_frame->get_renderer()->render($child); + + // Check for end render callback + $this->_check_callbacks("end_page_render", $child); + + if ($next_child) { + $this->_frame->next_page(); + } + + // Wait to dispose of all frames on the previous page + // so callback will have access to them + if ($prev_child) { + $prev_child->dispose(true); + } + $prev_child = $child; + $child = $next_child; + $current_page++; + } + + // Dispose of previous page if it still exists + if ($prev_child) { + $prev_child->dispose(true); + } + } + + //........................................................................ + + /** + * Check for callbacks that need to be performed when a given event + * gets triggered on a page + * + * @param string $event the type of event + * @param Frame $frame the frame that event is triggered on + */ + protected function _check_callbacks($event, $frame) + { + if (!isset($this->_callbacks)) { + $dompdf = $this->_frame->get_dompdf(); + $this->_callbacks = $dompdf->get_callbacks(); + $this->_canvas = $dompdf->get_canvas(); + } + + if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) { + $info = array( + 0 => $this->_canvas, "canvas" => $this->_canvas, + 1 => $frame, "frame" => $frame, + ); + $fs = $this->_callbacks[$event]; + foreach ($fs as $f) { + if (is_callable($f)) { + if (is_array($f)) { + $f[0]->{$f[1]}($info); + } else { + $f($info); + } + } + } + } + } +} diff --git a/library/vendor/dompdf/src/FrameReflower/Table.php b/library/vendor/dompdf/src/FrameReflower/Table.php new file mode 100644 index 000000000..a3626d8c7 --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/Table.php @@ -0,0 +1,587 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\Table as TableFrameDecorator; + +/** + * Reflows tables + * + * @access private + * @package dompdf + */ +class Table extends AbstractFrameReflower +{ + /** + * Frame for this reflower + * + * @var TableFrameDecorator + */ + protected $_frame; + + /** + * Cache of results between call to get_min_max_width and assign_widths + * + * @var array + */ + protected $_state; + + function __construct(TableFrameDecorator $frame) + { + $this->_state = null; + parent::__construct($frame); + } + + /** + * State is held here so it needs to be reset along with the decorator + */ + function reset() + { + $this->_state = null; + $this->_min_max_cache = null; + } + + //........................................................................ + + protected function _assign_widths() + { + $style = $this->_frame->get_style(); + + // Find the min/max width of the table and sort the columns into + // absolute/percent/auto arrays + $min_width = $this->_state["min_width"]; + $max_width = $this->_state["max_width"]; + $percent_used = $this->_state["percent_used"]; + $absolute_used = $this->_state["absolute_used"]; + $auto_min = $this->_state["auto_min"]; + + $absolute =& $this->_state["absolute"]; + $percent =& $this->_state["percent"]; + $auto =& $this->_state["auto"]; + + // Determine the actual width of the table + $cb = $this->_frame->get_containing_block(); + $columns =& $this->_frame->get_cellmap()->get_columns(); + + $width = $style->width; + + // Calculate padding & border fudge factor + $left = $style->margin_left; + $right = $style->margin_right; + + $centered = ($left === "auto" && $right === "auto"); + + $left = $left === "auto" ? 0 : $style->length_in_pt($left, $cb["w"]); + $right = $right === "auto" ? 0 : $style->length_in_pt($right, $cb["w"]); + + $delta = $left + $right; + + if (!$centered) { + $delta += $style->length_in_pt(array( + $style->padding_left, + $style->border_left_width, + $style->border_right_width, + $style->padding_right), + $cb["w"]); + } + + $min_table_width = $style->length_in_pt($style->min_width, $cb["w"] - $delta); + + // min & max widths already include borders & padding + $min_width -= $delta; + $max_width -= $delta; + + if ($width !== "auto") { + + $preferred_width = $style->length_in_pt($width, $cb["w"]) - $delta; + + if ($preferred_width < $min_table_width) + $preferred_width = $min_table_width; + + if ($preferred_width > $min_width) + $width = $preferred_width; + else + $width = $min_width; + + } else { + + if ($max_width + $delta < $cb["w"]) + $width = $max_width; + else if ($cb["w"] - $delta > $min_width) + $width = $cb["w"] - $delta; + else + $width = $min_width; + + if ($width < $min_table_width) + $width = $min_table_width; + + } + + // Store our resolved width + $style->width = $width; + + $cellmap = $this->_frame->get_cellmap(); + + if ($cellmap->is_columns_locked()) { + return; + } + + // If the whole table fits on the page, then assign each column it's max width + if ($width == $max_width) { + + foreach (array_keys($columns) as $i) + $cellmap->set_column_width($i, $columns[$i]["max-width"]); + + return; + } + + // Determine leftover and assign it evenly to all columns + if ($width > $min_width) { + + // We have four cases to deal with: + // + // 1. All columns are auto--no widths have been specified. In this + // case we distribute extra space across all columns weighted by max-width. + // + // 2. Only absolute widths have been specified. In this case we + // distribute any extra space equally among 'width: auto' columns, or all + // columns if no auto columns have been specified. + // + // 3. Only percentage widths have been specified. In this case we + // normalize the percentage values and distribute any remaining % to + // width: auto columns. We then proceed to assign widths as fractions + // of the table width. + // + // 4. Both absolute and percentage widths have been specified. + + $increment = 0; + + // Case 1: + if ($absolute_used == 0 && $percent_used == 0) { + $increment = $width - $min_width; + + foreach (array_keys($columns) as $i) { + $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment * ($columns[$i]["max-width"] / $max_width)); + } + return; + } + + + // Case 2 + if ($absolute_used > 0 && $percent_used == 0) { + + if (count($auto) > 0) + $increment = ($width - $auto_min - $absolute_used) / count($auto); + + // Use the absolutely specified width or the increment + foreach (array_keys($columns) as $i) { + + if ($columns[$i]["absolute"] > 0 && count($auto)) + $cellmap->set_column_width($i, $columns[$i]["min-width"]); + else if (count($auto)) + $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment); + else { + // All absolute columns + $increment = ($width - $absolute_used) * $columns[$i]["absolute"] / $absolute_used; + + $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment); + } + + } + return; + } + + + // Case 3: + if ($absolute_used == 0 && $percent_used > 0) { + + $scale = null; + $remaining = null; + + // Scale percent values if the total percentage is > 100, or if all + // values are specified as percentages. + if ($percent_used > 100 || count($auto) == 0) + $scale = 100 / $percent_used; + else + $scale = 1; + + // Account for the minimum space used by the unassigned auto columns + $used_width = $auto_min; + + foreach ($percent as $i) { + $columns[$i]["percent"] *= $scale; + + $slack = $width - $used_width; + + $w = min($columns[$i]["percent"] * $width / 100, $slack); + + if ($w < $columns[$i]["min-width"]) + $w = $columns[$i]["min-width"]; + + $cellmap->set_column_width($i, $w); + $used_width += $w; + + } + + // This works because $used_width includes the min-width of each + // unassigned column + if (count($auto) > 0) { + $increment = ($width - $used_width) / count($auto); + + foreach ($auto as $i) + $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment); + + } + return; + } + + // Case 4: + + // First-come, first served + if ($absolute_used > 0 && $percent_used > 0) { + + $used_width = $auto_min; + + foreach ($absolute as $i) { + $cellmap->set_column_width($i, $columns[$i]["min-width"]); + $used_width += $columns[$i]["min-width"]; + } + + // Scale percent values if the total percentage is > 100 or there + // are no auto values to take up slack + if ($percent_used > 100 || count($auto) == 0) + $scale = 100 / $percent_used; + else + $scale = 1; + + $remaining_width = $width - $used_width; + + foreach ($percent as $i) { + $slack = $remaining_width - $used_width; + + $columns[$i]["percent"] *= $scale; + $w = min($columns[$i]["percent"] * $remaining_width / 100, $slack); + + if ($w < $columns[$i]["min-width"]) + $w = $columns[$i]["min-width"]; + + $columns[$i]["used-width"] = $w; + $used_width += $w; + } + + if (count($auto) > 0) { + $increment = ($width - $used_width) / count($auto); + + foreach ($auto as $i) + $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment); + + } + + return; + } + + + } else { // we are over constrained + + // Each column gets its minimum width + foreach (array_keys($columns) as $i) + $cellmap->set_column_width($i, $columns[$i]["min-width"]); + + } + } + + //........................................................................ + + // Determine the frame's height based on min/max height + protected function _calculate_height() + { + + $style = $this->_frame->get_style(); + $height = $style->height; + + $cellmap = $this->_frame->get_cellmap(); + $cellmap->assign_frame_heights(); + $rows = $cellmap->get_rows(); + + // Determine our content height + $content_height = 0; + foreach ($rows as $r) + $content_height += $r["height"]; + + $cb = $this->_frame->get_containing_block(); + + if (!($style->overflow === "visible" || + ($style->overflow === "hidden" && $height === "auto")) + ) { + + // Only handle min/max height if the height is independent of the frame's content + + $min_height = $style->min_height; + $max_height = $style->max_height; + + if (isset($cb["h"])) { + $min_height = $style->length_in_pt($min_height, $cb["h"]); + $max_height = $style->length_in_pt($max_height, $cb["h"]); + + } else if (isset($cb["w"])) { + + if (mb_strpos($min_height, "%") !== false) + $min_height = 0; + else + $min_height = $style->length_in_pt($min_height, $cb["w"]); + + if (mb_strpos($max_height, "%") !== false) + $max_height = "none"; + else + $max_height = $style->length_in_pt($max_height, $cb["w"]); + } + + if ($max_height !== "none" && $min_height > $max_height) + // Swap 'em + list($max_height, $min_height) = array($min_height, $max_height); + + if ($max_height !== "none" && $height > $max_height) + $height = $max_height; + + if ($height < $min_height) + $height = $min_height; + + } else { + + // Use the content height or the height value, whichever is greater + if ($height !== "auto") { + $height = $style->length_in_pt($height, $cb["h"]); + + if ($height <= $content_height) + $height = $content_height; + else + $cellmap->set_frame_heights($height, $content_height); + + } else + $height = $content_height; + + } + + return $height; + + } + //........................................................................ + + /** + * @param BlockFrameDecorator $block + */ + function reflow(BlockFrameDecorator $block = null) + { + /** @var TableFrameDecorator */ + $frame = $this->_frame; + + // Check if a page break is forced + $page = $frame->get_root(); + $page->check_forced_page_break($frame); + + // Bail if the page is full + if ($page->is_full()) + return; + + // Let the page know that we're reflowing a table so that splits + // are suppressed (simply setting page-break-inside: avoid won't + // work because we may have an arbitrary number of block elements + // inside tds.) + $page->table_reflow_start(); + + // Collapse vertical margins, if required + $this->_collapse_margins(); + + $frame->position(); + + // Table layout algorithm: + // http://www.w3.org/TR/CSS21/tables.html#auto-table-layout + + if (is_null($this->_state)) + $this->get_min_max_width(); + + $cb = $frame->get_containing_block(); + $style = $frame->get_style(); + + // This is slightly inexact, but should be okay. Add half the + // border-spacing to the table as padding. The other half is added to + // the cells themselves. + if ($style->border_collapse === "separate") { + list($h, $v) = $style->border_spacing; + + $v = $style->length_in_pt($v) / 2; + $h = $style->length_in_pt($h) / 2; + + $style->padding_left = $style->length_in_pt($style->padding_left, $cb["w"]) + $h; + $style->padding_right = $style->length_in_pt($style->padding_right, $cb["w"]) + $h; + $style->padding_top = $style->length_in_pt($style->padding_top, $cb["h"]) + $v; + $style->padding_bottom = $style->length_in_pt($style->padding_bottom, $cb["h"]) + $v; + + } + + $this->_assign_widths(); + + // Adjust left & right margins, if they are auto + $width = $style->width; + $left = $style->margin_left; + $right = $style->margin_right; + + $diff = $cb["w"] - $width; + + if ($left === "auto" && $right === "auto") { + if ($diff < 0) { + $left = 0; + $right = $diff; + } else { + $left = $right = $diff / 2; + } + + $style->margin_left = "$left pt"; + $style->margin_right = "$right pt"; + + } else { + if ($left === "auto") { + $left = $style->length_in_pt($cb["w"] - $right - $width, $cb["w"]); + } + if ($right === "auto") { + $left = $style->length_in_pt($left, $cb["w"]); + } + } + + list($x, $y) = $frame->get_position(); + + // Determine the content edge + $content_x = $x + $left + $style->length_in_pt(array($style->padding_left, + $style->border_left_width), $cb["w"]); + $content_y = $y + $style->length_in_pt(array($style->margin_top, + $style->border_top_width, + $style->padding_top), $cb["h"]); + + if (isset($cb["h"])) + $h = $cb["h"]; + else + $h = null; + + $cellmap = $frame->get_cellmap(); + $col =& $cellmap->get_column(0); + $col["x"] = $content_x; + + $row =& $cellmap->get_row(0); + $row["y"] = $content_y; + + $cellmap->assign_x_positions(); + + // Set the containing block of each child & reflow + foreach ($frame->get_children() as $child) { + + // Bail if the page is full + if (!$page->in_nested_table() && $page->is_full()) + break; + + $child->set_containing_block($content_x, $content_y, $width, $h); + $child->reflow(); + + if (!$page->in_nested_table()) + // Check if a split has occured + $page->check_page_break($child); + + } + + // Assign heights to our cells: + $style->height = $this->_calculate_height(); + + if ($style->border_collapse === "collapse") { + // Unset our borders because our cells are now using them + $style->border_style = "none"; + } + + $page->table_reflow_end(); + + // Debugging: + //echo ($this->_frame->get_cellmap()); + + if ($block && $style->float === "none" && $frame->is_in_flow()) { + $block->add_frame_to_line($frame); + $block->add_line(); + } + } + + //........................................................................ + + function get_min_max_width() + { + + if (!is_null($this->_min_max_cache)) + return $this->_min_max_cache; + + $style = $this->_frame->get_style(); + + $this->_frame->normalise(); + + // Add the cells to the cellmap (this will calcluate column widths as + // frames are added) + $this->_frame->get_cellmap()->add_frame($this->_frame); + + // Find the min/max width of the table and sort the columns into + // absolute/percent/auto arrays + $this->_state = array(); + $this->_state["min_width"] = 0; + $this->_state["max_width"] = 0; + + $this->_state["percent_used"] = 0; + $this->_state["absolute_used"] = 0; + $this->_state["auto_min"] = 0; + + $this->_state["absolute"] = array(); + $this->_state["percent"] = array(); + $this->_state["auto"] = array(); + + $columns =& $this->_frame->get_cellmap()->get_columns(); + foreach (array_keys($columns) as $i) { + $this->_state["min_width"] += $columns[$i]["min-width"]; + $this->_state["max_width"] += $columns[$i]["max-width"]; + + if ($columns[$i]["absolute"] > 0) { + $this->_state["absolute"][] = $i; + $this->_state["absolute_used"] += $columns[$i]["absolute"]; + + } else if ($columns[$i]["percent"] > 0) { + $this->_state["percent"][] = $i; + $this->_state["percent_used"] += $columns[$i]["percent"]; + + } else { + $this->_state["auto"][] = $i; + $this->_state["auto_min"] += $columns[$i]["min-width"]; + } + } + + // Account for margins & padding + $dims = array($style->border_left_width, + $style->border_right_width, + $style->padding_left, + $style->padding_right, + $style->margin_left, + $style->margin_right); + + if ($style->border_collapse !== "collapse") + list($dims[]) = $style->border_spacing; + + $delta = $style->length_in_pt($dims, $this->_frame->get_containing_block("w")); + + $this->_state["min_width"] += $delta; + $this->_state["max_width"] += $delta; + + return $this->_min_max_cache = array( + $this->_state["min_width"], + $this->_state["max_width"], + "min" => $this->_state["min_width"], + "max" => $this->_state["max_width"], + ); + } +} diff --git a/library/vendor/dompdf/src/FrameReflower/TableCell.php b/library/vendor/dompdf/src/FrameReflower/TableCell.php new file mode 100644 index 000000000..0dc74a1f3 --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/TableCell.php @@ -0,0 +1,120 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\Table as TableFrameDecorator; + +/** + * Reflows table cells + * + * @package dompdf + */ +class TableCell extends Block +{ + function __construct(BlockFrameDecorator $frame) + { + parent::__construct($frame); + } + + function reflow(BlockFrameDecorator $block = null) + { + + $style = $this->_frame->get_style(); + + $table = TableFrameDecorator::find_parent_table($this->_frame); + $cellmap = $table->get_cellmap(); + + list($x, $y) = $cellmap->get_frame_position($this->_frame); + $this->_frame->set_position($x, $y); + + $cells = $cellmap->get_spanned_cells($this->_frame); + + $w = 0; + foreach ($cells["columns"] as $i) { + $col = $cellmap->get_column($i); + $w += $col["used-width"]; + } + + //FIXME? + $h = $this->_frame->get_containing_block("h"); + + $left_space = $style->length_in_pt(array($style->margin_left, + $style->padding_left, + $style->border_left_width), + $w); + + $right_space = $style->length_in_pt(array($style->padding_right, + $style->margin_right, + $style->border_right_width), + $w); + + $top_space = $style->length_in_pt(array($style->margin_top, + $style->padding_top, + $style->border_top_width), + $h); + $bottom_space = $style->length_in_pt(array($style->margin_bottom, + $style->padding_bottom, + $style->border_bottom_width), + $h); + + $style->width = $cb_w = $w - $left_space - $right_space; + + $content_x = $x + $left_space; + $content_y = $line_y = $y + $top_space; + + // Adjust the first line based on the text-indent property + $indent = $style->length_in_pt($style->text_indent, $w); + $this->_frame->increase_line_width($indent); + + $page = $this->_frame->get_root(); + + // Set the y position of the first line in the cell + $line_box = $this->_frame->get_current_line_box(); + $line_box->y = $line_y; + + // Set the containing blocks and reflow each child + foreach ($this->_frame->get_children() as $child) { + + if ($page->is_full()) + break; + + $child->set_containing_block($content_x, $content_y, $cb_w, $h); + + $this->process_clear($child); + + $child->reflow($this->_frame); + + $this->process_float($child, $x + $left_space, $w - $right_space - $left_space); + } + + // Determine our height + $style_height = $style->length_in_pt($style->height, $h); + + $this->_frame->set_content_height($this->_calculate_content_height()); + + $height = max($style_height, $this->_frame->get_content_height()); + + // Let the cellmap know our height + $cell_height = $height / count($cells["rows"]); + + if ($style_height <= $height) + $cell_height += $top_space + $bottom_space; + + foreach ($cells["rows"] as $i) + $cellmap->set_row_height($i, $cell_height); + + $style->height = $height; + + $this->_text_align(); + + $this->vertical_align(); + + } + +} diff --git a/library/vendor/dompdf/src/FrameReflower/TableRow.php b/library/vendor/dompdf/src/FrameReflower/TableRow.php new file mode 100644 index 000000000..7f2de4cff --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/TableRow.php @@ -0,0 +1,68 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\Table as TableFrameDecorator; +use Dompdf\FrameDecorator\TableRow as TableRowFrameDecorator; +use Dompdf\Exception; + +/** + * Reflows table rows + * + * @package dompdf + */ +class TableRow extends AbstractFrameReflower +{ + function __construct(TableRowFrameDecorator $frame) + { + parent::__construct($frame); + } + + //........................................................................ + + function reflow(BlockFrameDecorator $block = null) + { + $page = $this->_frame->get_root(); + + if ($page->is_full()) + return; + + $this->_frame->position(); + $style = $this->_frame->get_style(); + $cb = $this->_frame->get_containing_block(); + + foreach ($this->_frame->get_children() as $child) { + + if ($page->is_full()) + return; + + $child->set_containing_block($cb); + $child->reflow(); + + } + + if ($page->is_full()) + return; + + $table = TableFrameDecorator::find_parent_table($this->_frame); + $cellmap = $table->get_cellmap(); + $style->width = $cellmap->get_frame_width($this->_frame); + $style->height = $cellmap->get_frame_height($this->_frame); + + $this->_frame->set_position($cellmap->get_frame_position($this->_frame)); + + } + + //........................................................................ + + function get_min_max_width() + { + throw new Exception("Min/max width is undefined for table rows"); + } +} diff --git a/library/vendor/dompdf/src/FrameReflower/TableRowGroup.php b/library/vendor/dompdf/src/FrameReflower/TableRowGroup.php new file mode 100644 index 000000000..eb13dd599 --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/TableRowGroup.php @@ -0,0 +1,65 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\Table as TableFrameDecorator; + +/** + * Reflows table row groups (e.g. tbody tags) + * + * @package dompdf + */ +class TableRowGroup extends AbstractFrameReflower +{ + + function __construct($frame) + { + parent::__construct($frame); + } + + function reflow(BlockFrameDecorator $block = null) + { + $page = $this->_frame->get_root(); + + $style = $this->_frame->get_style(); + + // Our width is equal to the width of our parent table + $table = TableFrameDecorator::find_parent_table($this->_frame); + + $cb = $this->_frame->get_containing_block(); + + foreach ($this->_frame->get_children() as $child) { + // Bail if the page is full + if ($page->is_full()) + return; + + $child->set_containing_block($cb["x"], $cb["y"], $cb["w"], $cb["h"]); + $child->reflow(); + + // Check if a split has occured + $page->check_page_break($child); + + } + + if ($page->is_full()) + return; + + $cellmap = $table->get_cellmap(); + $style->width = $cellmap->get_frame_width($this->_frame); + $style->height = $cellmap->get_frame_height($this->_frame); + + $this->_frame->set_position($cellmap->get_frame_position($this->_frame)); + + if ($table->get_style()->border_collapse === "collapse") + // Unset our borders because our cells are now using them + $style->border_style = "none"; + + } + +} diff --git a/library/vendor/dompdf/src/FrameReflower/Text.php b/library/vendor/dompdf/src/FrameReflower/Text.php new file mode 100644 index 000000000..950f7fae9 --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/Text.php @@ -0,0 +1,487 @@ + + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\Text as TextFrameDecorator; +use Dompdf\FontMetrics; + +/** + * Reflows text frames. + * + * @package dompdf + */ +class Text extends AbstractFrameReflower +{ + + /** + * @var BlockFrameDecorator + */ + protected $_block_parent; // Nearest block-level ancestor + + /** + * @var TextFrameDecorator + */ + protected $_frame; + + public static $_whitespace_pattern = "/[ \t\r\n\f]+/u"; + + /** + * @var FontMetrics + */ + private $fontMetrics; + + /** + * @param TextFrameDecorator $frame + * @param FontMetrics $fontMetrics + */ + public function __construct(TextFrameDecorator $frame, FontMetrics $fontMetrics) + { + parent::__construct($frame); + $this->setFontMetrics($fontMetrics); + } + + //........................................................................ + + protected function _collapse_white_space($text) + { + //$text = $this->_frame->get_text(); +// if ( $this->_block_parent->get_current_line_box->w == 0 ) +// $text = ltrim($text, " \n\r\t"); + return preg_replace(self::$_whitespace_pattern, " ", $text); + } + + //........................................................................ + + protected function _line_break($text) + { + $style = $this->_frame->get_style(); + $size = $style->font_size; + $font = $style->font_family; + $current_line = $this->_block_parent->get_current_line_box(); + + // Determine the available width + $line_width = $this->_frame->get_containing_block("w"); + $current_line_width = $current_line->left + $current_line->w + $current_line->right; + + $available_width = $line_width - $current_line_width; + + // Account for word-spacing + $word_spacing = $style->length_in_pt($style->word_spacing); + $char_spacing = $style->length_in_pt($style->letter_spacing); + + // Determine the frame width including margin, padding & border + $text_width = $this->getFontMetrics()->getTextWidth($text, $font, $size, $word_spacing, $char_spacing); + $mbp_width = + $style->length_in_pt(array($style->margin_left, + $style->border_left_width, + $style->padding_left, + $style->padding_right, + $style->border_right_width, + $style->margin_right), $line_width); + + $frame_width = $text_width + $mbp_width; + +// Debugging: +// Helpers::pre_r("Text: '" . htmlspecialchars($text). "'"); +// Helpers::pre_r("width: " .$frame_width); +// Helpers::pre_r("textwidth + delta: $text_width + $mbp_width"); +// Helpers::pre_r("font-size: $size"); +// Helpers::pre_r("cb[w]: " .$line_width); +// Helpers::pre_r("available width: " . $available_width); +// Helpers::pre_r("current line width: " . $current_line_width); + +// Helpers::pre_r($words); + + if ($frame_width <= $available_width) + return false; + + // split the text into words + $words = preg_split('/([\s-]+)/u', $text, -1, PREG_SPLIT_DELIM_CAPTURE); + $wc = count($words); + + // Determine the split point + $width = 0; + $str = ""; + reset($words); + + // @todo support , + for ($i = 0; $i < $wc; $i += 2) { + $word = $words[$i] . (isset($words[$i + 1]) ? $words[$i + 1] : ""); + $word_width = $this->getFontMetrics()->getTextWidth($word, $font, $size, $word_spacing, $char_spacing); + if ($width + $word_width + $mbp_width > $available_width) + break; + + $width += $word_width; + $str .= $word; + } + + $break_word = ($style->word_wrap === "break-word"); + + // The first word has overflowed. Force it onto the line + if ($current_line_width == 0 && $width == 0) { + + $s = ""; + $last_width = 0; + + if ($break_word) { + for ($j = 0; $j < strlen($word); $j++) { + $s .= $word[$j]; + $_width = $this->getFontMetrics()->getTextWidth($s, $font, $size, $word_spacing, $char_spacing); + if ($_width > $available_width) { + break; + } + + $last_width = $_width; + } + } + + if ($break_word && $last_width > 0) { + $width += $last_width; + $str .= substr($s, 0, -1); + } else { + $width += $word_width; + $str .= $word; + } + } + + $offset = mb_strlen($str); + +// More debugging: +// var_dump($str); +// print_r("Width: ". $width); +// print_r("Offset: " . $offset); + + return $offset; + + } + + //........................................................................ + + protected function _newline_break($text) + { + + if (($i = mb_strpos($text, "\n")) === false) + return false; + + return $i + 1; + + } + + //........................................................................ + + protected function _layout_line() + { + $frame = $this->_frame; + $style = $frame->get_style(); + $text = $frame->get_text(); + $size = $style->font_size; + $font = $style->font_family; + + // Determine the text height + $style->height = $this->getFontMetrics()->getFontHeight($font, $size); + + $split = false; + $add_line = false; + + // Handle text transform: + // http://www.w3.org/TR/CSS21/text.html#propdef-text-transform + switch (strtolower($style->text_transform)) { + default: + break; + case "capitalize": + $text = mb_convert_case($text, MB_CASE_TITLE); + break; + case "uppercase": + $text = mb_convert_case($text, MB_CASE_UPPER); + break; + case "lowercase": + $text = mb_convert_case($text, MB_CASE_LOWER); + break; + } + + // Handle white-space property: + // http://www.w3.org/TR/CSS21/text.html#propdef-white-space + switch ($style->white_space) { + + default: + case "normal": + $frame->set_text($text = $this->_collapse_white_space($text)); + if ($text == "") + break; + + $split = $this->_line_break($text); + break; + + case "pre": + $split = $this->_newline_break($text); + $add_line = $split !== false; + break; + + case "nowrap": + $frame->set_text($text = $this->_collapse_white_space($text)); + break; + + case "pre-wrap": + $split = $this->_newline_break($text); + + if (($tmp = $this->_line_break($text)) !== false) { + $add_line = $split < $tmp; + $split = min($tmp, $split); + } else + $add_line = true; + + break; + + case "pre-line": + // Collapse white-space except for \n + $frame->set_text($text = preg_replace("/[ \t]+/u", " ", $text)); + + if ($text == "") + break; + + $split = $this->_newline_break($text); + + if (($tmp = $this->_line_break($text)) !== false) { + $add_line = $split < $tmp; + $split = min($tmp, $split); + } else + $add_line = true; + + break; + + } + + // Handle degenerate case + if ($text === "") + return; + + if ($split !== false) { + + // Handle edge cases + if ($split == 0 && $text === " ") { + $frame->set_text(""); + return; + } + + if ($split == 0) { + + // Trim newlines from the beginning of the line + //$this->_frame->set_text(ltrim($text, "\n\r")); + + $this->_block_parent->add_line(); + $frame->position(); + + // Layout the new line + $this->_layout_line(); + + } else if ($split < mb_strlen($frame->get_text())) { + // split the line if required + $frame->split_text($split); + + $t = $frame->get_text(); + + // Remove any trailing newlines + if ($split > 1 && $t[$split - 1] === "\n" && !$frame->is_pre()) + $frame->set_text(mb_substr($t, 0, -1)); + + // Do we need to trim spaces on wrapped lines? This might be desired, however, we + // can't trim the lines here or the layout will be affected if trimming the line + // leaves enough space to fit the next word in the text stream (because pdf layout + // is performed elsewhere). + /*if (!$this->_frame->get_prev_sibling() && !$this->_frame->get_next_sibling()) { + $t = $this->_frame->get_text(); + $this->_frame->set_text( trim($t) ); + }*/ + } + + if ($add_line) { + $this->_block_parent->add_line(); + $frame->position(); + } + + } else { + + // Remove empty space from start and end of line, but only where there isn't an inline sibling + // and the parent node isn't an inline element with siblings + // FIXME: Include non-breaking spaces? + $t = $frame->get_text(); + $parent = $frame->get_parent(); + $is_inline_frame = get_class($parent) === 'Inline_Frame_Decorator'; + + if ((!$is_inline_frame && !$frame->get_next_sibling()) /* || + ( $is_inline_frame && !$parent->get_next_sibling())*/ + ) { // fails BOLD UNDERLINED becomes BOLDUNDERLINED + $t = rtrim($t); + } + + if ((!$is_inline_frame && !$frame->get_prev_sibling()) /* || + ( $is_inline_frame && !$parent->get_prev_sibling())*/ + ) { // AB C fails (the whitespace is removed) + $t = ltrim($t); + } + + $frame->set_text($t); + + } + + // Set our new width + $width = $frame->recalculate_width(); + } + + //........................................................................ + + function reflow(BlockFrameDecorator $block = null) + { + $frame = $this->_frame; + $page = $frame->get_root(); + $page->check_forced_page_break($this->_frame); + + if ($page->is_full()) + return; + + $this->_block_parent = /*isset($block) ? $block : */ + $frame->find_block_parent(); + + // Left trim the text if this is the first text on the line and we're + // collapsing white space +// if ( $this->_block_parent->get_current_line()->w == 0 && +// ($frame->get_style()->white_space !== "pre" || +// $frame->get_style()->white_space !== "pre-wrap") ) { +// $frame->set_text( ltrim( $frame->get_text() ) ); +// } + + $frame->position(); + + $this->_layout_line(); + + if ($block) { + $block->add_frame_to_line($frame); + } + } + + //........................................................................ + + // Returns an array(0 => min, 1 => max, "min" => min, "max" => max) of the + // minimum and maximum widths of this frame + function get_min_max_width() + { + /*if ( !is_null($this->_min_max_cache) ) + return $this->_min_max_cache;*/ + $frame = $this->_frame; + $style = $frame->get_style(); + $this->_block_parent = $frame->find_block_parent(); + $line_width = $frame->get_containing_block("w"); + + $str = $text = $frame->get_text(); + $size = $style->font_size; + $font = $style->font_family; + + $word_spacing = $style->length_in_pt($style->word_spacing); + $char_spacing = $style->length_in_pt($style->letter_spacing); + + switch ($style->white_space) { + + default: + case "normal": + $str = preg_replace(self::$_whitespace_pattern, " ", $str); + case "pre-wrap": + case "pre-line": + + // Find the longest word (i.e. minimum length) + + // This technique (using arrays & an anonymous function) is actually + // faster than doing a single-pass character by character scan. Heh, + // yes I took the time to bench it ;) + $words = array_flip(preg_split("/[\s-]+/u", $str, -1, PREG_SPLIT_DELIM_CAPTURE)); + $root = $this; + array_walk($words, function(&$val, $str) use ($font, $size, $word_spacing, $char_spacing, $root) { + $val = $root->getFontMetrics()->getTextWidth($str, $font, $size, $word_spacing, $char_spacing); + }); + + arsort($words); + $min = reset($words); + break; + + case "pre": + $lines = array_flip(preg_split("/\n/u", $str)); + $root = $this; + array_walk($lines, function(&$val, $str) use ($font, $size, $word_spacing, $char_spacing, $root) { + $val = $root->getFontMetrics()->getTextWidth($str, $font, $size, $word_spacing, $char_spacing); + }); + + arsort($lines); + $min = reset($lines); + break; + + case "nowrap": + $min = $this->getFontMetrics()->getTextWidth($this->_collapse_white_space($str), $font, $size, $word_spacing, $char_spacing); + break; + + } + + switch ($style->white_space) { + + default: + case "normal": + case "nowrap": + $str = preg_replace(self::$_whitespace_pattern, " ", $text); + break; + + case "pre-line": + //XXX: Is this correct? + $str = preg_replace("/[ \t]+/u", " ", $text); + + case "pre-wrap": + // Find the longest word (i.e. minimum length) + $lines = array_flip(preg_split("/\n/", $text)); + $root = $this; + array_walk($lines, function(&$val, $str) use ($font, $size, $word_spacing, $char_spacing, $root) { + $val = $root->getFontMetrics()->getTextWidth($str, $font, $size, $word_spacing, $char_spacing); + }); + arsort($lines); + reset($lines); + $str = key($lines); + break; + + } + + $max = $this->getFontMetrics()->getTextWidth($str, $font, $size, $word_spacing, $char_spacing); + + $delta = $style->length_in_pt(array($style->margin_left, + $style->border_left_width, + $style->padding_left, + $style->padding_right, + $style->border_right_width, + $style->margin_right), $line_width); + $min += $delta; + $max += $delta; + + return $this->_min_max_cache = array($min, $max, "min" => $min, "max" => $max); + + } + + /** + * @param FontMetrics $fontMetrics + * @return $this + */ + public function setFontMetrics(FontMetrics $fontMetrics) + { + $this->fontMetrics = $fontMetrics; + return $this; + } + + /** + * @return FontMetrics + */ + public function getFontMetrics() + { + return $this->fontMetrics; + } +} diff --git a/library/vendor/dompdf/src/Helpers.php b/library/vendor/dompdf/src/Helpers.php new file mode 100644 index 000000000..5b19f6812 --- /dev/null +++ b/library/vendor/dompdf/src/Helpers.php @@ -0,0 +1,733 @@ + tags if the current sapi is not 'cli'. + * Returns the output string instead of displaying it if $return is true. + * + * @param mixed $mixed variable or expression to display + * @param bool $return + * + * @return string + */ + public static function pre_r($mixed, $return = false) + { + if ($return) { + return "
" . print_r($mixed, true) . "
"; + } + + if (php_sapi_name() !== "cli") { + echo "
";
+        }
+
+        print_r($mixed);
+
+        if (php_sapi_name() !== "cli") {
+            echo "
"; + } else { + echo "\n"; + } + + flush(); + } + + /** + * builds a full url given a protocol, hostname, base path and url + * + * @param string $protocol + * @param string $host + * @param string $base_path + * @param string $url + * @return string + * + * Initially the trailing slash of $base_path was optional, and conditionally appended. + * However on dynamically created sites, where the page is given as url parameter, + * the base path might not end with an url. + * Therefore do not append a slash, and **require** the $base_url to ending in a slash + * when needed. + * Vice versa, on using the local file system path of a file, make sure that the slash + * is appended (o.k. also for Windows) + */ + public static function build_url($protocol, $host, $base_path, $url) + { + $protocol = mb_strtolower($protocol); + if (strlen($url) == 0) { + //return $protocol . $host . rtrim($base_path, "/\\") . "/"; + return $protocol . $host . $base_path; + } + + // Is the url already fully qualified, a Data URI, or a reference to a named anchor? + if (mb_strpos($url, "://") !== false || mb_substr($url, 0, 1) === "#" || mb_strpos($url, "data:") === 0 || mb_strpos($url, "mailto:") === 0) { + return $url; + } + + $ret = $protocol; + + if (!in_array(mb_strtolower($protocol), array("http://", "https://", "ftp://", "ftps://"))) { + //On Windows local file, an abs path can begin also with a '\' or a drive letter and colon + //drive: followed by a relative path would be a drive specific default folder. + //not known in php app code, treat as abs path + //($url[1] !== ':' || ($url[2]!=='\\' && $url[2]!=='/')) + if ($url[0] !== '/' && (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' || ($url[0] !== '\\' && $url[1] !== ':'))) { + // For rel path and local acess we ignore the host, and run the path through realpath() + $ret .= realpath($base_path) . '/'; + } + $ret .= $url; + $ret = preg_replace('/\?(.*)$/', "", $ret); + return $ret; + } + + // Protocol relative urls (e.g. "//example.org/style.css") + if (strpos($url, '//') === 0) { + $ret .= substr($url, 2); + //remote urls with backslash in html/css are not really correct, but lets be genereous + } elseif ($url[0] === '/' || $url[0] === '\\') { + // Absolute path + $ret .= $host . $url; + } else { + // Relative path + //$base_path = $base_path !== "" ? rtrim($base_path, "/\\") . "/" : ""; + $ret .= $host . $base_path . $url; + } + + return $ret; + } + + /** + * Converts decimal numbers to roman numerals + * + * @param int $num + * + * @throws Exception + * @return string + */ + public static function dec2roman($num) + { + + static $ones = array("", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"); + static $tens = array("", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"); + static $hund = array("", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"); + static $thou = array("", "m", "mm", "mmm"); + + if (!is_numeric($num)) { + throw new Exception("dec2roman() requires a numeric argument."); + } + + if ($num > 4000 || $num < 0) { + return "(out of range)"; + } + + $num = strrev((string)$num); + + $ret = ""; + switch (mb_strlen($num)) { + case 4: + $ret .= $thou[$num[3]]; + case 3: + $ret .= $hund[$num[2]]; + case 2: + $ret .= $tens[$num[1]]; + case 1: + $ret .= $ones[$num[0]]; + default: + break; + } + + return $ret; + } + + /** + * Determines whether $value is a percentage or not + * + * @param float $value + * + * @return bool + */ + public static function is_percent($value) + { + return false !== mb_strpos($value, "%"); + } + + /** + * Parses a data URI scheme + * http://en.wikipedia.org/wiki/Data_URI_scheme + * + * @param string $data_uri The data URI to parse + * + * @return array The result with charset, mime type and decoded data + */ + public static function parse_data_uri($data_uri) + { + if (!preg_match('/^data:(?P[a-z0-9\/+-.]+)(;charset=(?P[a-z0-9-])+)?(?P;base64)?\,(?P.*)?/is', $data_uri, $match)) { + return false; + } + + $match['data'] = rawurldecode($match['data']); + $result = array( + 'charset' => $match['charset'] ? $match['charset'] : 'US-ASCII', + 'mime' => $match['mime'] ? $match['mime'] : 'text/plain', + 'data' => $match['base64'] ? base64_decode($match['data']) : $match['data'], + ); + + return $result; + } + + /** + * Decoder for RLE8 compression in windows bitmaps + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp + * + * @param string $str Data to decode + * @param integer $width Image width + * + * @return string + */ + public static function rle8_decode($str, $width) + { + $lineWidth = $width + (3 - ($width - 1) % 4); + $out = ''; + $cnt = strlen($str); + + for ($i = 0; $i < $cnt; $i++) { + $o = ord($str[$i]); + switch ($o) { + case 0: # ESCAPE + $i++; + switch (ord($str[$i])) { + case 0: # NEW LINE + $padCnt = $lineWidth - strlen($out) % $lineWidth; + if ($padCnt < $lineWidth) $out .= str_repeat(chr(0), $padCnt); # pad line + break; + case 1: # END OF FILE + $padCnt = $lineWidth - strlen($out) % $lineWidth; + if ($padCnt < $lineWidth) $out .= str_repeat(chr(0), $padCnt); # pad line + break 3; + case 2: # DELTA + $i += 2; + break; + default: # ABSOLUTE MODE + $num = ord($str[$i]); + for ($j = 0; $j < $num; $j++) + $out .= $str[++$i]; + if ($num % 2) $i++; + } + break; + default: + $out .= str_repeat($str[++$i], $o); + } + } + return $out; + } + + /** + * Decoder for RLE4 compression in windows bitmaps + * see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp + * + * @param string $str Data to decode + * @param integer $width Image width + * + * @return string + */ + public static function rle4_decode($str, $width) + { + $w = floor($width / 2) + ($width % 2); + $lineWidth = $w + (3 - (($width - 1) / 2) % 4); + $pixels = array(); + $cnt = strlen($str); + $c = 0; + + for ($i = 0; $i < $cnt; $i++) { + $o = ord($str[$i]); + switch ($o) { + case 0: # ESCAPE + $i++; + switch (ord($str[$i])) { + case 0: # NEW LINE + while (count($pixels) % $lineWidth != 0) { + $pixels[] = 0; + } + break; + case 1: # END OF FILE + while (count($pixels) % $lineWidth != 0) { + $pixels[] = 0; + } + break 3; + case 2: # DELTA + $i += 2; + break; + default: # ABSOLUTE MODE + $num = ord($str[$i]); + for ($j = 0; $j < $num; $j++) { + if ($j % 2 == 0) { + $c = ord($str[++$i]); + $pixels[] = ($c & 240) >> 4; + } else { + $pixels[] = $c & 15; + } + } + + if ($num % 2 == 0) { + $i++; + } + } + break; + default: + $c = ord($str[++$i]); + for ($j = 0; $j < $o; $j++) { + $pixels[] = ($j % 2 == 0 ? ($c & 240) >> 4 : $c & 15); + } + } + } + + $out = ''; + if (count($pixels) % 2) { + $pixels[] = 0; + } + + $cnt = count($pixels) / 2; + + for ($i = 0; $i < $cnt; $i++) { + $out .= chr(16 * $pixels[2 * $i] + $pixels[2 * $i + 1]); + } + + return $out; + } + + /** + * parse a full url or pathname and return an array(protocol, host, path, + * file + query + fragment) + * + * @param string $url + * @return array + */ + public static function explode_url($url) + { + $protocol = ""; + $host = ""; + $path = ""; + $file = ""; + + $arr = parse_url($url); + if ( isset($arr["scheme"]) ) { + $arr["scheme"] = mb_strtolower($arr["scheme"]); + } + + // Exclude windows drive letters... + if (isset($arr["scheme"]) && $arr["scheme"] !== "file" && strlen($arr["scheme"]) > 1) { + $protocol = $arr["scheme"] . "://"; + + if (isset($arr["user"])) { + $host .= $arr["user"]; + + if (isset($arr["pass"])) { + $host .= ":" . $arr["pass"]; + } + + $host .= "@"; + } + + if (isset($arr["host"])) { + $host .= $arr["host"]; + } + + if (isset($arr["port"])) { + $host .= ":" . $arr["port"]; + } + + if (isset($arr["path"]) && $arr["path"] !== "") { + // Do we have a trailing slash? + if ($arr["path"][mb_strlen($arr["path"]) - 1] === "/") { + $path = $arr["path"]; + $file = ""; + } else { + $path = rtrim(dirname($arr["path"]), '/\\') . "/"; + $file = basename($arr["path"]); + } + } + + if (isset($arr["query"])) { + $file .= "?" . $arr["query"]; + } + + if (isset($arr["fragment"])) { + $file .= "#" . $arr["fragment"]; + } + + } else { + + $i = mb_stripos($url, "file://"); + if ($i !== false) { + $url = mb_substr($url, $i + 7); + } + + $protocol = ""; // "file://"; ? why doesn't this work... It's because of + // network filenames like //COMPU/SHARENAME + + $host = ""; // localhost, really + $file = basename($url); + + $path = dirname($url); + + // Check that the path exists + if ($path !== false) { + $path .= '/'; + + } else { + // generate a url to access the file if no real path found. + $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://'; + + $host = isset($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"] : php_uname("n"); + + if (substr($arr["path"], 0, 1) === '/') { + $path = dirname($arr["path"]); + } else { + $path = '/' . rtrim(dirname($_SERVER["SCRIPT_NAME"]), '/') . '/' . $arr["path"]; + } + } + } + + $ret = array($protocol, $host, $path, $file, + "protocol" => $protocol, + "host" => $host, + "path" => $path, + "file" => $file); + return $ret; + } + + /** + * Print debug messages + * + * @param string $type The type of debug messages to print + * @param string $msg The message to show + */ + public static function dompdf_debug($type, $msg) + { + global $_DOMPDF_DEBUG_TYPES, $_dompdf_show_warnings, $_dompdf_debug; + if (isset($_DOMPDF_DEBUG_TYPES[$type]) && ($_dompdf_show_warnings || $_dompdf_debug)) { + $arr = debug_backtrace(); + + echo basename($arr[0]["file"]) . " (" . $arr[0]["line"] . "): " . $arr[1]["function"] . ": "; + Helpers::pre_r($msg); + } + } + + /** + * Stores warnings in an array for display later + * This function allows warnings generated by the DomDocument parser + * and CSS loader ({@link Stylesheet}) to be captured and displayed + * later. Without this function, errors are displayed immediately and + * PDF streaming is impossible. + * @see http://www.php.net/manual/en/function.set-error_handler.php + * + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param string $errline + * + * @throws Exception + */ + public static function record_warnings($errno, $errstr, $errfile, $errline) + { + // Not a warning or notice + if (!($errno & (E_WARNING | E_NOTICE | E_USER_NOTICE | E_USER_WARNING))) { + throw new Exception($errstr . " $errno"); + } + + global $_dompdf_warnings; + global $_dompdf_show_warnings; + + if ($_dompdf_show_warnings) { + echo $errstr . "\n"; + } + + $_dompdf_warnings[] = $errstr; + } + + /** + * @param $c + * @return bool|string + */ + public static function unichr($c) + { + if ($c <= 0x7F) { + return chr($c); + } else if ($c <= 0x7FF) { + return chr(0xC0 | $c >> 6) . chr(0x80 | $c & 0x3F); + } else if ($c <= 0xFFFF) { + return chr(0xE0 | $c >> 12) . chr(0x80 | $c >> 6 & 0x3F) + . chr(0x80 | $c & 0x3F); + } else if ($c <= 0x10FFFF) { + return chr(0xF0 | $c >> 18) . chr(0x80 | $c >> 12 & 0x3F) + . chr(0x80 | $c >> 6 & 0x3F) + . chr(0x80 | $c & 0x3F); + } + return false; + } + + /** + * Converts a CMYK color to RGB + * + * @param float|float[] $c + * @param float $m + * @param float $y + * @param float $k + * + * @return float[] + */ + public static function cmyk_to_rgb($c, $m = null, $y = null, $k = null) + { + if (is_array($c)) { + list($c, $m, $y, $k) = $c; + } + + $c *= 255; + $m *= 255; + $y *= 255; + $k *= 255; + + $r = (1 - round(2.55 * ($c + $k))); + $g = (1 - round(2.55 * ($m + $k))); + $b = (1 - round(2.55 * ($y + $k))); + + if ($r < 0) $r = 0; + if ($g < 0) $g = 0; + if ($b < 0) $b = 0; + + return array( + $r, $g, $b, + "r" => $r, "g" => $g, "b" => $b + ); + } + + /** + * getimagesize doesn't give a good size for 32bit BMP image v5 + * + * @param string $filename + * @return array The same format as getimagesize($filename) + */ + public static function dompdf_getimagesize($filename, $context = null) + { + static $cache = array(); + + if (isset($cache[$filename])) { + return $cache[$filename]; + } + + list($width, $height, $type) = getimagesize($filename); + + // Custom types + $types = array( + IMAGETYPE_JPEG => "jpeg", + IMAGETYPE_GIF => "gif", + IMAGETYPE_BMP => "bmp", + IMAGETYPE_PNG => "png", + ); + + $type = isset($types[$type]) ? $types[$type] : null; + + if ($width == null || $height == null) { + $data = file_get_contents($filename, null, $context, 0, 26); + + if (substr($data, 0, 2) === "BM") { + $meta = unpack('vtype/Vfilesize/Vreserved/Voffset/Vheadersize/Vwidth/Vheight', $data); + $width = (int)$meta['width']; + $height = (int)$meta['height']; + $type = "bmp"; + } + else { + if (strpos(file_get_contents($filename), "loadFile($filename); + + list($width, $height) = $doc->getDimensions(); + $type = "svg"; + } + } + + } + + return $cache[$filename] = array($width, $height, $type); + } + + /** + * Credit goes to mgutt + * http://www.programmierer-forum.de/function-imagecreatefrombmp-welche-variante-laeuft-t143137.htm + * Modified by Fabien Menager to support RGB555 BMP format + */ + public static function imagecreatefrombmp($filename, $context = null) + { + if (!function_exists("imagecreatetruecolor")) { + trigger_error("The PHP GD extension is required, but is not installed.", E_ERROR); + return false; + } + + // version 1.00 + if (!($fh = fopen($filename, 'rb'))) { + trigger_error('imagecreatefrombmp: Can not open ' . $filename, E_USER_WARNING); + return false; + } + + $bytes_read = 0; + + // read file header + $meta = unpack('vtype/Vfilesize/Vreserved/Voffset', fread($fh, 14)); + + // check for bitmap + if ($meta['type'] != 19778) { + trigger_error('imagecreatefrombmp: ' . $filename . ' is not a bitmap!', E_USER_WARNING); + return false; + } + + // read image header + $meta += unpack('Vheadersize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vcolors/Vimportant', fread($fh, 40)); + $bytes_read += 40; + + // read additional bitfield header + if ($meta['compression'] == 3) { + $meta += unpack('VrMask/VgMask/VbMask', fread($fh, 12)); + $bytes_read += 12; + } + + // set bytes and padding + $meta['bytes'] = $meta['bits'] / 8; + $meta['decal'] = 4 - (4 * (($meta['width'] * $meta['bytes'] / 4) - floor($meta['width'] * $meta['bytes'] / 4))); + if ($meta['decal'] == 4) { + $meta['decal'] = 0; + } + + // obtain imagesize + if ($meta['imagesize'] < 1) { + $meta['imagesize'] = $meta['filesize'] - $meta['offset']; + // in rare cases filesize is equal to offset so we need to read physical size + if ($meta['imagesize'] < 1) { + $meta['imagesize'] = @filesize($filename) - $meta['offset']; + if ($meta['imagesize'] < 1) { + trigger_error('imagecreatefrombmp: Can not obtain filesize of ' . $filename . '!', E_USER_WARNING); + return false; + } + } + } + + // calculate colors + $meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors']; + + // read color palette + $palette = array(); + if ($meta['bits'] < 16) { + $palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4)); + // in rare cases the color value is signed + if ($palette[1] < 0) { + foreach ($palette as $i => $color) { + $palette[$i] = $color + 16777216; + } + } + } + + // ignore extra bitmap headers + if ($meta['headersize'] > $bytes_read) { + fread($fh, $meta['headersize'] - $bytes_read); + } + + // create gd image + $im = imagecreatetruecolor($meta['width'], $meta['height']); + $data = fread($fh, $meta['imagesize']); + + // uncompress data + switch ($meta['compression']) { + case 1: + $data = Helpers::rle8_decode($data, $meta['width']); + break; + case 2: + $data = Helpers::rle4_decode($data, $meta['width']); + break; + } + + $p = 0; + $vide = chr(0); + $y = $meta['height'] - 1; + $error = 'imagecreatefrombmp: ' . $filename . ' has not enough data!'; + + // loop through the image data beginning with the lower left corner + while ($y >= 0) { + $x = 0; + while ($x < $meta['width']) { + switch ($meta['bits']) { + case 32: + case 24: + if (!($part = substr($data, $p, 3 /*$meta['bytes']*/))) { + trigger_error($error, E_USER_WARNING); + return $im; + } + $color = unpack('V', $part . $vide); + break; + case 16: + if (!($part = substr($data, $p, 2 /*$meta['bytes']*/))) { + trigger_error($error, E_USER_WARNING); + return $im; + } + $color = unpack('v', $part); + + if (empty($meta['rMask']) || $meta['rMask'] != 0xf800) { + $color[1] = (($color[1] & 0x7c00) >> 7) * 65536 + (($color[1] & 0x03e0) >> 2) * 256 + (($color[1] & 0x001f) << 3); // 555 + } else { + $color[1] = (($color[1] & 0xf800) >> 8) * 65536 + (($color[1] & 0x07e0) >> 3) * 256 + (($color[1] & 0x001f) << 3); // 565 + } + break; + case 8: + $color = unpack('n', $vide . substr($data, $p, 1)); + $color[1] = $palette[$color[1] + 1]; + break; + case 4: + $color = unpack('n', $vide . substr($data, floor($p), 1)); + $color[1] = ($p * 2) % 2 == 0 ? $color[1] >> 4 : $color[1] & 0x0F; + $color[1] = $palette[$color[1] + 1]; + break; + case 1: + $color = unpack('n', $vide . substr($data, floor($p), 1)); + switch (($p * 8) % 8) { + case 0: + $color[1] = $color[1] >> 7; + break; + case 1: + $color[1] = ($color[1] & 0x40) >> 6; + break; + case 2: + $color[1] = ($color[1] & 0x20) >> 5; + break; + case 3: + $color[1] = ($color[1] & 0x10) >> 4; + break; + case 4: + $color[1] = ($color[1] & 0x8) >> 3; + break; + case 5: + $color[1] = ($color[1] & 0x4) >> 2; + break; + case 6: + $color[1] = ($color[1] & 0x2) >> 1; + break; + case 7: + $color[1] = ($color[1] & 0x1); + break; + } + $color[1] = $palette[$color[1] + 1]; + break; + default: + trigger_error('imagecreatefrombmp: ' . $filename . ' has ' . $meta['bits'] . ' bits and this is not supported!', E_USER_WARNING); + return false; + } + imagesetpixel($im, $x, $y, $color[1]); + $x++; + $p += $meta['bytes']; + } + $y--; + $p += $meta['decal']; + } + fclose($fh); + return $im; + } + +} diff --git a/library/vendor/dompdf/src/Image/Cache.php b/library/vendor/dompdf/src/Image/Cache.php new file mode 100644 index 000000000..8e9e70e49 --- /dev/null +++ b/library/vendor/dompdf/src/Image/Cache.php @@ -0,0 +1,186 @@ + + * @author Helmut Tischer + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Image; + +use Dompdf\Dompdf; +use Dompdf\Helpers; +use Dompdf\Exception\ImageException; + +/** + * Static class that resolves image urls and downloads and caches + * remote images if required. + * + * @package dompdf + */ +class Cache +{ + /** + * Array of downloaded images. Cached so that identical images are + * not needlessly downloaded. + * + * @var array + */ + protected static $_cache = array(); + + /** + * The url to the "broken image" used when images can't be loaded + * + * @var string + */ + public static $broken_image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABABAMAAABYR2ztAAAAA3NCSVQICAjb4U/gAAAAHlBMVEWZmZn////g4OCkpKS1tbXv7++9vb2tra3m5ub5+fkFnN6oAAAACXBIWXMAAAsSAAALEgHS3X78AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M0BrLToAAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNC8xMi8xMRPnI58AAAGZSURBVEiJhZbPasMwDMbTw2DHKhDQcbDQPsEge4BAjg0Mxh5gkBcY7Niwkpx32PvOjv9JspX60It/+fxJsqxW1b11gN11rA7N3v6vAd5nfR9fDYCTDiyzAeA6qgKd9QDNoAtsAKyKCxzAAfhdBuyHGwC3oovNvQOaxxJwnSNg3ZQFAlBy4ax7AG6ZBLrgA5Cn038SAPgREiaJHJASwXYEhEQQIACyikTTCWCBJJoANBfpPAKQdBLHFMBYkctcBKIE9lAGggt6gRjgA2GV44CL7m1WgS08fAAdsPHxyyMAIyHujgRwEldHArCKy5cBz90+gNOyf8TTyKOUQN2LPEmgnWWPcKD+sr+rnuqTK1avAcHfRSv3afTgVAbqmCPiggLtGM8aSkBNOidVjADrmIDYebT1PoGsWJEE8Oc0b96aZoe4iMBZPiADB6RAzEUA2vwRmyiAL3Lfv6MBSEmUEg7ALt/3LhxwLgj4QNw4UCbKEsaBNpPsyRbgVRASFig78BIGyJNIJQyQTwIi0RvgT98H+Mi6W67j3X8H/427u5bfpQGVAAAAAElFTkSuQmCC"; + + /** + * Current dompdf instance + * + * @var Dompdf + */ + protected static $_dompdf; + + /** + * Resolve and fetch an image for use. + * + * @param string $url The url of the image + * @param string $protocol Default protocol if none specified in $url + * @param string $host Default host if none specified in $url + * @param string $base_path Default path if none specified in $url + * @param Dompdf $dompdf The Dompdf instance + * + * @throws ImageException + * @return array An array with two elements: The local path to the image and the image extension + */ + static function resolve_url($url, $protocol, $host, $base_path, Dompdf $dompdf) + { + self::$_dompdf = $dompdf; + + $protocol = mb_strtolower($protocol); + $parsed_url = Helpers::explode_url($url); + $message = null; + + $remote = ($protocol && $protocol !== "file://") || ($parsed_url['protocol'] != ""); + + $data_uri = strpos($parsed_url['protocol'], "data:") === 0; + $full_url = null; + $enable_remote = $dompdf->get_option("enable_remote"); + + try { + + // Remote not allowed and is not DataURI + if (!$enable_remote && $remote && !$data_uri) { + throw new ImageException("Remote file access is disabled.", E_WARNING); + } // Remote allowed or DataURI + else { + if ($enable_remote && $remote || $data_uri) { + // Download remote files to a temporary directory + $full_url = Helpers::build_url($protocol, $host, $base_path, $url); + + // From cache + if (isset(self::$_cache[$full_url])) { + $resolved_url = self::$_cache[$full_url]; + } // From remote + else { + $tmp_dir = $dompdf->get_option("temp_dir"); + $resolved_url = tempnam($tmp_dir, "ca_dompdf_img_"); + $image = ""; + + if ($data_uri) { + if ($parsed_data_uri = Helpers::parse_data_uri($url)) { + $image = $parsed_data_uri['data']; + } + } else { + set_error_handler(array("\\Dompdf\\Helpers", "record_warnings")); + $image = file_get_contents($full_url, null, $dompdf->getHttpContext()); + restore_error_handler(); + } + + // Image not found or invalid + if (strlen($image) == 0) { + $msg = ($data_uri ? "Data-URI could not be parsed" : "Image not found"); + throw new ImageException($msg, E_WARNING); + } // Image found, put in cache and process + else { + //e.g. fetch.php?media=url.jpg&cache=1 + //- Image file name might be one of the dynamic parts of the url, don't strip off! + //- a remote url does not need to have a file extension at all + //- local cached file does not have a matching file extension + //Therefore get image type from the content + file_put_contents($resolved_url, $image); + } + } + } // Not remote, local image + else { + $resolved_url = Helpers::build_url($protocol, $host, $base_path, $url); + } + } + + // Check if the local file is readable + if (!is_readable($resolved_url) || !filesize($resolved_url)) { + throw new ImageException("Image not readable or empty", E_WARNING); + } // Check is the file is an image + else { + list($width, $height, $type) = Helpers::dompdf_getimagesize($resolved_url, $dompdf->getHttpContext()); + + // Known image type + if ($width && $height && in_array($type, array("gif", "png", "jpeg", "bmp", "svg"))) { + //Don't put replacement image into cache - otherwise it will be deleted on cache cleanup. + //Only execute on successful caching of remote image. + if ($enable_remote && $remote || $data_uri) { + self::$_cache[$full_url] = $resolved_url; + } + } // Unknown image type + else { + throw new ImageException("Image type unknown", E_WARNING); + } + } + } catch (ImageException $e) { + $resolved_url = self::$broken_image; + $type = "png"; + $message = "Image not found or type unknown"; + Helpers::record_warnings($e->getCode(), $e->getMessage() . " \n $url", $e->getFile(), $e->getLine()); + } + + return array($resolved_url, $type, $message); + } + + /** + * Unlink all cached images (i.e. temporary images either downloaded + * or converted) + */ + static function clear() + { + if (empty(self::$_cache) || self::$_dompdf->get_option("debugKeepTemp")) { + return; + } + + foreach (self::$_cache as $file) { + if (self::$_dompdf->get_option("debugPng")) { + print "[clear unlink $file]"; + } + unlink($file); + } + + self::$_cache = array(); + } + + static function detect_type($file, $context = null) + { + list(, , $type) = Helpers::dompdf_getimagesize($file, $context); + + return $type; + } + + static function is_broken($url) + { + return $url === self::$broken_image; + } +} + +if (file_exists(realpath(__DIR__ . "/../../lib/res/broken_image.png"))) { + Cache::$broken_image = realpath(__DIR__ . "/../../lib/res/broken_image.png"); +} \ No newline at end of file diff --git a/library/vendor/dompdf/src/JavascriptEmbedder.php b/library/vendor/dompdf/src/JavascriptEmbedder.php new file mode 100644 index 000000000..1463f2315 --- /dev/null +++ b/library/vendor/dompdf/src/JavascriptEmbedder.php @@ -0,0 +1,43 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf; + +use Dompdf\Frame; + +/** + * Embeds Javascript into the PDF document + * + * @package dompdf + */ +class JavascriptEmbedder +{ + + /** + * @var Dompdf + */ + protected $_dompdf; + + function __construct(Dompdf $dompdf) + { + $this->_dompdf = $dompdf; + } + + function insert($script) + { + $this->_dompdf->get_canvas()->javascript($script); + } + + function render(Frame $frame) + { + if (!$this->_dompdf->get_option("enable_javascript")) { + return; + } + + $this->insert($frame->get_node()->nodeValue); + } +} diff --git a/library/vendor/dompdf/src/LineBox.php b/library/vendor/dompdf/src/LineBox.php new file mode 100644 index 000000000..b94e08917 --- /dev/null +++ b/library/vendor/dompdf/src/LineBox.php @@ -0,0 +1,260 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf; + +use Dompdf\Frame; +use Dompdf\FrameDecorator\Block; +use Dompdf\FrameDecorator\Page; + +/** + * The line box class + * + * This class represents a line box + * http://www.w3.org/TR/CSS2/visuren.html#line-box + * + * @package dompdf + */ +class LineBox +{ + + /** + * @var Block + */ + protected $_block_frame; + + /** + * @var Frame[] + */ + protected $_frames = array(); + + /** + * @var integer + */ + public $wc = 0; + + /** + * @var float + */ + public $y = null; + + /** + * @var float + */ + public $w = 0.0; + + /** + * @var float + */ + public $h = 0.0; + + /** + * @var float + */ + public $left = 0.0; + + /** + * @var float + */ + public $right = 0.0; + + /** + * @var Frame + */ + public $tallest_frame = null; + + /** + * @var bool[] + */ + public $floating_blocks = array(); + + /** + * @var bool + */ + public $br = false; + + /** + * Class constructor + * + * @param Block $frame the Block containing this line + */ + function __construct(Block $frame, $y = 0) + { + $this->_block_frame = $frame; + $this->_frames = array(); + $this->y = $y; + + $this->get_float_offsets(); + } + + /** + * Returns the floating elements inside the first floating parent + * + * @param Page $root + * + * @return Frame[] + */ + function get_floats_inside(Page $root) + { + $floating_frames = $root->get_floating_frames(); + + if (count($floating_frames) == 0) { + return $floating_frames; + } + + // Find nearest floating element + $p = $this->_block_frame; + while ($p->get_style()->float === "none") { + $parent = $p->get_parent(); + + if (!$parent) { + break; + } + + $p = $parent; + } + + if ($p == $root) { + return $floating_frames; + } + + $parent = $p; + + $childs = array(); + + foreach ($floating_frames as $_floating) { + $p = $_floating->get_parent(); + + while (($p = $p->get_parent()) && $p !== $parent) ; + + if ($p) { + $childs[] = $p; + } + } + + return $childs; + } + + function get_float_offsets() + { + static $anti_infinite_loop = 500; // FIXME smelly hack + + $reflower = $this->_block_frame->get_reflower(); + + if (!$reflower) { + return; + } + + $cb_w = null; + + $block = $this->_block_frame; + $root = $block->get_root(); + + if (!$root) { + return; + } + + $floating_frames = $this->get_floats_inside($root); + + foreach ($floating_frames as $child_key => $floating_frame) { + $id = $floating_frame->get_id(); + + if (isset($this->floating_blocks[$id])) { + continue; + } + + $floating_style = $floating_frame->get_style(); + $float = $floating_style->float; + + $floating_width = $floating_frame->get_margin_width(); + + if (!$cb_w) { + $cb_w = $floating_frame->get_containing_block("w"); + } + + $line_w = $this->get_width(); + + if (!$floating_frame->_float_next_line && ($cb_w <= $line_w + $floating_width) && ($cb_w > $line_w)) { + $floating_frame->_float_next_line = true; + continue; + } + + // If the child is still shifted by the floating element + if ($anti_infinite_loop-- > 0 && + $floating_frame->get_position("y") + $floating_frame->get_margin_height() > $this->y && + $block->get_position("x") + $block->get_margin_width() > $floating_frame->get_position("x") + ) { + if ($float === "left") { + $this->left += $floating_width; + } else { + $this->right += $floating_width; + } + + $this->floating_blocks[$id] = true; + } // else, the floating element won't shift anymore + else { + $root->remove_floating_frame($child_key); + } + } + } + + /** + * @return float + */ + function get_width() + { + return $this->left + $this->w + $this->right; + } + + /** + * @return Block + */ + function get_block_frame() + { + return $this->_block_frame; + } + + /** + * @return Frame[] + */ + function &get_frames() + { + return $this->_frames; + } + + /** + * @param Frame $frame + */ + function add_frame(Frame $frame) + { + $this->_frames[] = $frame; + } + + function __toString() + { + $props = array("wc", "y", "w", "h", "left", "right", "br"); + $s = ""; + foreach ($props as $prop) { + $s .= "$prop: " . $this->$prop . "\n"; + } + $s .= count($this->_frames) . " frames\n"; + + return $s; + } + /*function __get($prop) { + if (!isset($this->{"_$prop"})) return; + return $this->{"_$prop"}; + }*/ +} + +/* +class LineBoxList implements Iterator { + private $_p = 0; + private $_lines = array(); + +} +*/ diff --git a/library/vendor/dompdf/src/Options.php b/library/vendor/dompdf/src/Options.php new file mode 100644 index 000000000..d46914517 --- /dev/null +++ b/library/vendor/dompdf/src/Options.php @@ -0,0 +1,974 @@ + ... tags. + * + * ==== IMPORTANT ==== + * Enabling this for documents you do not trust (e.g. arbitrary remote html + * pages) is a security risk. Embedded scripts are run with the same level of + * system access available to dompdf. Set this option to false (recommended) + * if you wish to process untrusted documents. + * + * This setting may increase the risk of system exploit. Do not change + * this settings without understanding the consequences. Additional + * documentation is available on the dompdf wiki at: + * https://github.com/dompdf/dompdf/wiki + * + * @var bool + */ + private $isPhpEnabled = false; + + /** + * Enable remote file access + * + * If this setting is set to true, DOMPDF will access remote sites for + * images and CSS files as required. + * + * ==== IMPORTANT ==== + * This can be a security risk, in particular in combination with isPhpEnabled and + * allowing remote html code to be passed to $dompdf = new DOMPDF(); $dompdf->load_html(...); + * This allows anonymous users to download legally doubtful internet content which on + * tracing back appears to being downloaded by your server, or allows malicious php code + * in remote html pages to be executed by your server with your account privileges. + * + * This setting may increase the risk of system exploit. Do not change + * this settings without understanding the consequences. Additional + * documentation is available on the dompdf wiki at: + * https://github.com/dompdf/dompdf/wiki + * + * @var bool + */ + private $isRemoteEnabled = false; + + /** + * Enable inline Javascript + * + * If this setting is set to true then DOMPDF will automatically insert + * JavaScript code contained within tags. + * + * @var bool + */ + private $isJavascriptEnabled = true; + + /** + * Use the more-than-experimental HTML5 Lib parser + * + * @var bool + */ + private $isHtml5ParserEnabled = false; + + /** + * Whether to enable font subsetting or not. + * + * @var is_bool + */ + private $isFontSubsettingEnabled = false; + + /** + * @var bool + */ + private $debugPng = false; + + /** + * @var bool + */ + private $debugKeepTemp = false; + + /** + * @var bool + */ + private $debugCss = false; + + /** + * @var bool + */ + private $debugLayout = false; + + /** + * @var bool + */ + private $debugLayoutLines = true; + + /** + * @var bool + */ + private $debugLayoutBlocks = true; + + /** + * @var bool + */ + private $debugLayoutInline = true; + + /** + * @var bool + */ + private $debugLayoutPaddingBox = true; + + /** + * The PDF rendering backend to use + * + * Valid settings are 'PDFLib', 'CPDF', 'GD', and 'auto'. 'auto' will + * look for PDFLib and use it if found, or if not it will fall back on + * CPDF. 'GD' renders PDFs to graphic files. {@link Dompdf\CanvasFactory} + * ultimately determines which rendering class to instantiate + * based on this setting. + * + * @var string + */ + private $pdfBackend = "CPDF"; + + /** + * PDFlib license key + * + * If you are using a licensed, commercial version of PDFlib, specify + * your license key here. If you are using PDFlib-Lite or are evaluating + * the commercial version of PDFlib, comment out this setting. + * + * @link http://www.pdflib.com + * + * If pdflib present in web server and auto or selected explicitely above, + * a real license code must exist! + * + * @var string + */ + private $pdflibLicense = ""; + + /** + * @var string + * @deprecated + */ + private $adminUsername = "user"; + + /** + * @var string + * @deprecated + */ + private $adminPassword = "password"; + + /** + * @param array $attributes + */ + public function __construct(array $attributes = null) + { + $this->setChroot(realpath(__DIR__ . "/../")); + $this->setRootDir($this->getChroot()); + $this->setTempDir(sys_get_temp_dir()); + $this->setFontDir($this->chroot . DIRECTORY_SEPARATOR . "lib" . DIRECTORY_SEPARATOR . "fonts"); + $this->setFontCache($this->getFontDir()); + $this->setLogOutputFile($this->getTempDir() . DIRECTORY_SEPARATOR . "log.htm"); + + if (null !== $attributes) { + $this->set($attributes); + } + } + + /** + * @param array|string $attributes + * @param null|mixed $value + * @return $this + */ + public function set($attributes, $value = null) + { + if (!is_array($attributes)) { + $attributes = array($attributes => $value); + } + foreach ($attributes as $key => $value) { + if ($key === 'tempDir' || $key === 'temp_dir') { + $this->setTempDir($value); + } elseif ($key === 'fontDir' || $key === 'font_dir') { + $this->setFontDir($value); + } elseif ($key === 'fontCache' || $key === 'font_cache') { + $this->setFontCache($value); + } elseif ($key === 'chroot') { + $this->setChroot($value); + } elseif ($key === 'logOutputFile' || $key === 'log_output_file') { + $this->setLogOutputFile($value); + } elseif ($key === 'defaultMediaType' || $key === 'default_media_type') { + $this->setDefaultMediaType($value); + } elseif ($key === 'defaultPaperSize' || $key === 'default_paper_size') { + $this->setDefaultPaperSize($value); + } elseif ($key === 'defaultFont' || $key === 'default_font') { + $this->setDefaultFont($value); + } elseif ($key === 'dpi') { + $this->setDpi($value); + } elseif ($key === 'fontHeightRatio' || $key === 'font_height_ratio') { + $this->setFontHeightRatio($value); + } elseif ($key === 'isPhpEnabled' || $key === 'is_php_enabled' || $key === 'enable_php') { + $this->setIsPhpEnabled($value); + } elseif ($key === 'isRemoteEnabled' || $key === 'is_remote_enabled' || $key === 'enable_remote') { + $this->setIsRemoteEnabled($value); + } elseif ($key === 'isJavascriptEnabled' || $key === 'is_javascript_enabled' || $key === 'enable_javascript') { + $this->setIsJavascriptEnabled($value); + } elseif ($key === 'isHtml5ParserEnabled' || $key === 'is_html5_parser_enabled' || $key === 'enable_html5_parser') { + $this->setIsHtml5ParserEnabled($value); + } elseif ($key === 'isFontSubsettingEnabled' || $key === 'is_font_subsetting_enabled' || $key === 'enable_font_subsetting') { + $this->setIsFontSubsettingEnabled($value); + } elseif ($key === 'debugPng' || $key === 'debug_png') { + $this->setDebugPng($value); + } elseif ($key === 'debugKeepTemp' || $key === 'debug_keep_temp') { + $this->setDebugKeepTemp($value); + } elseif ($key === 'debugCss' || $key === 'debug_css') { + $this->setDebugCss($value); + } elseif ($key === 'debugLayout' || $key === 'debug_layout') { + $this->setDebugLayout($value); + } elseif ($key === 'debugLayoutLines' || $key === 'debug_layout_lines') { + $this->setDebugLayoutLines($value); + } elseif ($key === 'debugLayoutBlocks' || $key === 'debug_layout_blocks') { + $this->setDebugLayoutBlocks($value); + } elseif ($key === 'debugLayoutInline' || $key === 'debug_layout_inline') { + $this->setDebugLayoutInline($value); + } elseif ($key === 'debugLayoutPaddingBox' || $key === 'debug_layout_padding_box') { + $this->setDebugLayoutPaddingBox($value); + } elseif ($key === 'pdfBackend' || $key === 'pdf_backend') { + $this->setPdfBackend($value); + } elseif ($key === 'pdflibLicense' || $key === 'pdflib_license') { + $this->setPdflibLicense($value); + } elseif ($key === 'adminUsername' || $key === 'admin_username') { + $this->setAdminUsername($value); + } elseif ($key === 'adminPassword' || $key === 'admin_password') { + $this->setAdminPassword($value); + } + } + return $this; + } + + /** + * @param string $key + * @return mixed + */ + public function get($key) + { + if ($key === 'tempDir' || $key === 'temp_dir') { + return $this->getTempDir(); + } elseif ($key === 'fontDir' || $key === 'font_dir') { + return $this->getFontDir(); + } elseif ($key === 'fontCache' || $key === 'font_cache') { + return $this->getFontCache(); + } elseif ($key === 'chroot') { + return $this->getChroot(); + } elseif ($key === 'logOutputFile' || $key === 'log_output_file') { + return $this->getLogOutputFile(); + } elseif ($key === 'defaultMediaType' || $key === 'default_media_type') { + return $this->getDefaultMediaType(); + } elseif ($key === 'defaultPaperSize' || $key === 'default_paper_size') { + return $this->getDefaultPaperSize(); + } elseif ($key === 'defaultFont' || $key === 'default_font') { + return $this->getDefaultFont(); + } elseif ($key === 'dpi') { + return $this->getDpi(); + } elseif ($key === 'fontHeightRatio' || $key === 'font_height_ratio') { + return $this->getFontHeightRatio(); + } elseif ($key === 'isPhpEnabled' || $key === 'is_php_enabled' || $key === 'enable_php') { + return $this->getIsPhpEnabled(); + } elseif ($key === 'isRemoteEnabled' || $key === 'is_remote_enabled' || $key === 'enable_remote') { + return $this->getIsRemoteEnabled(); + } elseif ($key === 'isJavascriptEnabled' || $key === 'is_javascript_enabled' || $key === 'enable_javascript') { + return $this->getIsJavascriptEnabled(); + } elseif ($key === 'isHtml5ParserEnabled' || $key === 'is_html5_parser_enabled' || $key === 'enable_html5_parser') { + return $this->getIsHtml5ParserEnabled(); + } elseif ($key === 'isFontSubsettingEnabled' || $key === 'is_font_subsetting_enabled' || $key === 'enable_font_subsetting') { + return $this->getIsFontSubsettingEnabled(); + } elseif ($key === 'debugPng' || $key === 'debug_png') { + return $this->getDebugPng(); + } elseif ($key === 'debugKeepTemp' || $key === 'debug_keep_temp') { + return $this->getDebugKeepTemp(); + } elseif ($key === 'debugCss' || $key === 'debug_css') { + return $this->getDebugCss(); + } elseif ($key === 'debugLayout' || $key === 'debug_layout') { + return $this->getDebugLayout(); + } elseif ($key === 'debugLayoutLines' || $key === 'debug_layout_lines') { + return $this->getDebugLayoutLines(); + } elseif ($key === 'debugLayoutBlocks' || $key === 'debug_layout_blocks') { + return $this->getDebugLayoutBlocks(); + } elseif ($key === 'debugLayoutInline' || $key === 'debug_layout_inline') { + return $this->getDebugLayoutInline(); + } elseif ($key === 'debugLayoutPaddingBox' || $key === 'debug_layout_padding_box') { + return $this->getDebugLayoutPaddingBox(); + } elseif ($key === 'pdfBackend' || $key === 'pdf_backend') { + return $this->getPdfBackend(); + } elseif ($key === 'pdflibLicense' || $key === 'pdflib_license') { + return $this->getPdflibLicense(); + } elseif ($key === 'adminUsername' || $key === 'admin_username') { + return $this->getAdminUsername(); + } elseif ($key === 'adminPassword' || $key === 'admin_password') { + return $this->getAdminPassword(); + } + return null; + } + + /** + * @param string $adminPassword + * @return $this + */ + public function setAdminPassword($adminPassword) + { + $this->adminPassword = $adminPassword; + return $this; + } + + /** + * @return string + */ + public function getAdminPassword() + { + return $this->adminPassword; + } + + /** + * @param string $adminUsername + * @return $this + */ + public function setAdminUsername($adminUsername) + { + $this->adminUsername = $adminUsername; + return $this; + } + + /** + * @return string + */ + public function getAdminUsername() + { + return $this->adminUsername; + } + + /** + * @param string $pdfBackend + * @return $this + */ + public function setPdfBackend($pdfBackend) + { + $this->pdfBackend = $pdfBackend; + return $this; + } + + /** + * @return string + */ + public function getPdfBackend() + { + return $this->pdfBackend; + } + + /** + * @param string $pdflibLicense + * @return $this + */ + public function setPdflibLicense($pdflibLicense) + { + $this->pdflibLicense = $pdflibLicense; + return $this; + } + + /** + * @return string + */ + public function getPdflibLicense() + { + return $this->pdflibLicense; + } + + /** + * @param string $chroot + * @return $this + */ + public function setChroot($chroot) + { + $this->chroot = $chroot; + return $this; + } + + /** + * @return string + */ + public function getChroot() + { + return $this->chroot; + } + + /** + * @param boolean $debugCss + * @return $this + */ + public function setDebugCss($debugCss) + { + $this->debugCss = $debugCss; + return $this; + } + + /** + * @return boolean + */ + public function getDebugCss() + { + return $this->debugCss; + } + + /** + * @param boolean $debugKeepTemp + * @return $this + */ + public function setDebugKeepTemp($debugKeepTemp) + { + $this->debugKeepTemp = $debugKeepTemp; + return $this; + } + + /** + * @return boolean + */ + public function getDebugKeepTemp() + { + return $this->debugKeepTemp; + } + + /** + * @param boolean $debugLayout + * @return $this + */ + public function setDebugLayout($debugLayout) + { + $this->debugLayout = $debugLayout; + return $this; + } + + /** + * @return boolean + */ + public function getDebugLayout() + { + return $this->debugLayout; + } + + /** + * @param boolean $debugLayoutBlocks + * @return $this + */ + public function setDebugLayoutBlocks($debugLayoutBlocks) + { + $this->debugLayoutBlocks = $debugLayoutBlocks; + return $this; + } + + /** + * @return boolean + */ + public function getDebugLayoutBlocks() + { + return $this->debugLayoutBlocks; + } + + /** + * @param boolean $debugLayoutInline + * @return $this + */ + public function setDebugLayoutInline($debugLayoutInline) + { + $this->debugLayoutInline = $debugLayoutInline; + return $this; + } + + /** + * @return boolean + */ + public function getDebugLayoutInline() + { + return $this->debugLayoutInline; + } + + /** + * @param boolean $debugLayoutLines + * @return $this + */ + public function setDebugLayoutLines($debugLayoutLines) + { + $this->debugLayoutLines = $debugLayoutLines; + return $this; + } + + /** + * @return boolean + */ + public function getDebugLayoutLines() + { + return $this->debugLayoutLines; + } + + /** + * @param boolean $debugLayoutPaddingBox + * @return $this + */ + public function setDebugLayoutPaddingBox($debugLayoutPaddingBox) + { + $this->debugLayoutPaddingBox = $debugLayoutPaddingBox; + return $this; + } + + /** + * @return boolean + */ + public function getDebugLayoutPaddingBox() + { + return $this->debugLayoutPaddingBox; + } + + /** + * @param boolean $debugPng + * @return $this + */ + public function setDebugPng($debugPng) + { + $this->debugPng = $debugPng; + return $this; + } + + /** + * @return boolean + */ + public function getDebugPng() + { + return $this->debugPng; + } + + /** + * @param string $defaultFont + * @return $this + */ + public function setDefaultFont($defaultFont) + { + $this->defaultFont = $defaultFont; + return $this; + } + + /** + * @return string + */ + public function getDefaultFont() + { + return $this->defaultFont; + } + + /** + * @param string $defaultMediaType + * @return $this + */ + public function setDefaultMediaType($defaultMediaType) + { + $this->defaultMediaType = $defaultMediaType; + return $this; + } + + /** + * @return string + */ + public function getDefaultMediaType() + { + return $this->defaultMediaType; + } + + /** + * @param string $defaultPaperSize + * @return $this + */ + public function setDefaultPaperSize($defaultPaperSize) + { + $this->defaultPaperSize = $defaultPaperSize; + return $this; + } + + /** + * @return string + */ + public function getDefaultPaperSize() + { + return $this->defaultPaperSize; + } + + /** + * @param int $dpi + * @return $this + */ + public function setDpi($dpi) + { + $this->dpi = $dpi; + return $this; + } + + /** + * @return int + */ + public function getDpi() + { + return $this->dpi; + } + + /** + * @param string $fontCache + * @return $this + */ + public function setFontCache($fontCache) + { + $this->fontCache = $fontCache; + return $this; + } + + /** + * @return string + */ + public function getFontCache() + { + return $this->fontCache; + } + + /** + * @param string $fontDir + * @return $this + */ + public function setFontDir($fontDir) + { + $this->fontDir = $fontDir; + return $this; + } + + /** + * @return string + */ + public function getFontDir() + { + return $this->fontDir; + } + + /** + * @param float $fontHeightRatio + * @return $this + */ + public function setFontHeightRatio($fontHeightRatio) + { + $this->fontHeightRatio = $fontHeightRatio; + return $this; + } + + /** + * @return float + */ + public function getFontHeightRatio() + { + return $this->fontHeightRatio; + } + + /** + * @param boolean $isFontSubsettingEnabled + * @return $this + */ + public function setIsFontSubsettingEnabled($isFontSubsettingEnabled) + { + $this->isFontSubsettingEnabled = $isFontSubsettingEnabled; + return $this; + } + + /** + * @return boolean + */ + public function getIsFontSubsettingEnabled() + { + return $this->isFontSubsettingEnabled; + } + + /** + * @return boolean + */ + public function isFontSubsettingEnabled() + { + return $this->getIsFontSubsettingEnabled(); + } + + /** + * @param boolean $isHtml5ParserEnabled + * @return $this + */ + public function setIsHtml5ParserEnabled($isHtml5ParserEnabled) + { + $this->isHtml5ParserEnabled = $isHtml5ParserEnabled; + return $this; + } + + /** + * @return boolean + */ + public function getIsHtml5ParserEnabled() + { + return $this->isHtml5ParserEnabled; + } + + /** + * @return boolean + */ + public function isHtml5ParserEnabled() + { + return $this->getIsHtml5ParserEnabled(); + } + + /** + * @param boolean $isJavascriptEnabled + * @return $this + */ + public function setIsJavascriptEnabled($isJavascriptEnabled) + { + $this->isJavascriptEnabled = $isJavascriptEnabled; + return $this; + } + + /** + * @return boolean + */ + public function getIsJavascriptEnabled() + { + return $this->isJavascriptEnabled; + } + + /** + * @return boolean + */ + public function isJavascriptEnabled() + { + return $this->getIsJavascriptEnabled(); + } + + /** + * @param boolean $isPhpEnabled + * @return $this + */ + public function setIsPhpEnabled($isPhpEnabled) + { + $this->isPhpEnabled = $isPhpEnabled; + return $this; + } + + /** + * @return boolean + */ + public function getIsPhpEnabled() + { + return $this->isPhpEnabled; + } + + /** + * @return boolean + */ + public function isPhpEnabled() + { + return $this->getIsPhpEnabled(); + } + + /** + * @param boolean $isRemoteEnabled + * @return $this + */ + public function setIsRemoteEnabled($isRemoteEnabled) + { + $this->isRemoteEnabled = $isRemoteEnabled; + return $this; + } + + /** + * @return boolean + */ + public function getIsRemoteEnabled() + { + return $this->isRemoteEnabled; + } + + /** + * @return boolean + */ + public function isRemoteEnabled() + { + return $this->getIsRemoteEnabled(); + } + + /** + * @param string $logOutputFile + * @return $this + */ + public function setLogOutputFile($logOutputFile) + { + $this->logOutputFile = $logOutputFile; + return $this; + } + + /** + * @return string + */ + public function getLogOutputFile() + { + return $this->logOutputFile; + } + + /** + * @param string $tempDir + * @return $this + */ + public function setTempDir($tempDir) + { + $this->tempDir = $tempDir; + return $this; + } + + /** + * @return string + */ + public function getTempDir() + { + return $this->tempDir; + } + + /** + * @param string $rootDir + * @return $this + */ + public function setRootDir($rootDir) + { + $this->rootDir = $rootDir; + return $this; + } + + /** + * @return string + */ + public function getRootDir() + { + return $this->rootDir; + } +} \ No newline at end of file diff --git a/library/vendor/dompdf/src/PhpEvaluator.php b/library/vendor/dompdf/src/PhpEvaluator.php new file mode 100644 index 000000000..c7b877729 --- /dev/null +++ b/library/vendor/dompdf/src/PhpEvaluator.php @@ -0,0 +1,55 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf; + +use Dompdf\Frame; + +/** + * Executes inline PHP code during the rendering process + * + * @package dompdf + */ +class PhpEvaluator +{ + + /** + * @var Canvas + */ + protected $_canvas; + + function __construct(Canvas $canvas) + { + $this->_canvas = $canvas; + } + + function evaluate($code, $vars = array()) + { + if (!$this->_canvas->get_dompdf()->get_option("enable_php")) { + return; + } + + // Set up some variables for the inline code + $pdf = $this->_canvas; + $fontMetrics = $pdf->get_dompdf()->getFontMetrics(); + $PAGE_NUM = $pdf->get_page_number(); + $PAGE_COUNT = $pdf->get_page_count(); + + // Override those variables if passed in + foreach ($vars as $k => $v) { + $$k = $v; + } + + //$code = html_entity_decode($code); // @todo uncomment this when tested + eval($code); + } + + function render(Frame $frame) + { + $this->evaluate($frame->get_node()->nodeValue); + } +} diff --git a/library/vendor/dompdf/src/Positioner/Absolute.php b/library/vendor/dompdf/src/Positioner/Absolute.php new file mode 100644 index 000000000..4feae8208 --- /dev/null +++ b/library/vendor/dompdf/src/Positioner/Absolute.php @@ -0,0 +1,123 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\Positioner; + +use Dompdf\FrameDecorator\AbstractFrameDecorator; + +/** + * Positions absolutely positioned frames + */ +class Absolute extends AbstractPositioner +{ + + function __construct(AbstractFrameDecorator $frame) + { + parent::__construct($frame); + } + + function position() + { + + $frame = $this->_frame; + $style = $frame->get_style(); + + $p = $frame->find_positionned_parent(); + + list($x, $y, $w, $h) = $frame->get_containing_block(); + + $top = $style->length_in_pt($style->top, $h); + $right = $style->length_in_pt($style->right, $w); + $bottom = $style->length_in_pt($style->bottom, $h); + $left = $style->length_in_pt($style->left, $w); + + if ($p && !($left === "auto" && $right === "auto")) { + // Get the parent's padding box (see http://www.w3.org/TR/CSS21/visuren.html#propdef-top) + list($x, $y, $w, $h) = $p->get_padding_box(); + } + + list($width, $height) = array($frame->get_margin_width(), $frame->get_margin_height()); + + $orig_style = $this->_frame->get_original_style(); + $orig_width = $orig_style->width; + $orig_height = $orig_style->height; + + /**************************** + * + * Width auto: + * ____________| left=auto | left=fixed | + * right=auto | A | B | + * right=fixed | C | D | + * + * Width fixed: + * ____________| left=auto | left=fixed | + * right=auto | E | F | + * right=fixed | G | H | + *****************************/ + + if ($left === "auto") { + if ($right === "auto") { + // A or E - Keep the frame at the same position + $x = $x + $frame->find_block_parent()->get_current_line_box()->w; + } else { + if ($orig_width === "auto") { + // C + $x += $w - $width - $right; + } else { + // G + $x += $w - $width - $right; + } + } + } else { + if ($right === "auto") { + // B or F + $x += $left; + } else { + if ($orig_width === "auto") { + // D - TODO change width + $x += $left; + } else { + // H - Everything is fixed: left + width win + $x += $left; + } + } + } + + // The same vertically + if ($top === "auto") { + if ($bottom === "auto") { + // A or E - Keep the frame at the same position + $y = $frame->find_block_parent()->get_current_line_box()->y; + } else { + if ($orig_height === "auto") { + // C + $y += $h - $height - $bottom; + } else { + // G + $y += $h - $height - $bottom; + } + } + } else { + if ($bottom === "auto") { + // B or F + $y += $top; + } else { + if ($orig_height === "auto") { + // D - TODO change height + $y += $top; + } else { + // H - Everything is fixed: top + height win + $y += $top; + } + } + } + + $frame->set_position($x, $y); + + } +} \ No newline at end of file diff --git a/library/vendor/dompdf/src/Positioner/AbstractPositioner.php b/library/vendor/dompdf/src/Positioner/AbstractPositioner.php new file mode 100644 index 000000000..1c19356f8 --- /dev/null +++ b/library/vendor/dompdf/src/Positioner/AbstractPositioner.php @@ -0,0 +1,52 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\Positioner; + +use Dompdf\FrameDecorator\AbstractFrameDecorator; + +/** + * Base AbstractPositioner class + * + * Defines postioner interface + * + * @access private + * @package dompdf + */ +abstract class AbstractPositioner +{ + + /** + * @var \Dompdf\FrameDecorator\AbstractFrameDecorator + */ + protected $_frame; + + //........................................................................ + + function __construct(AbstractFrameDecorator $frame) + { + $this->_frame = $frame; + } + + //........................................................................ + + abstract function position(); + + function move($offset_x, $offset_y, $ignore_self = false) + { + list($x, $y) = $this->_frame->get_position(); + + if (!$ignore_self) { + $this->_frame->set_position($x + $offset_x, $y + $offset_y); + } + + foreach ($this->_frame->get_children() as $child) { + $child->move($offset_x, $offset_y); + } + } +} diff --git a/library/vendor/dompdf/src/Positioner/Block.php b/library/vendor/dompdf/src/Positioner/Block.php new file mode 100644 index 000000000..fdb418096 --- /dev/null +++ b/library/vendor/dompdf/src/Positioner/Block.php @@ -0,0 +1,63 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\Positioner; + +use Dompdf\FrameDecorator\AbstractFrameDecorator; + +/** + * Positions block frames + * + * @access private + * @package dompdf + */ +class Block extends AbstractPositioner { + + + function __construct(AbstractFrameDecorator $frame) + { + parent::__construct($frame); + } + + //........................................................................ + + function position() + { + $frame = $this->_frame; + $style = $frame->get_style(); + $cb = $frame->get_containing_block(); + $p = $frame->find_block_parent(); + + if ($p) { + $float = $style->float; + + if (!$float || $float === "none") { + $p->add_line(true); + } + $y = $p->get_current_line_box()->y; + + } else { + $y = $cb["y"]; + } + + $x = $cb["x"]; + + // Relative positionning + if ($style->position === "relative") { + $top = $style->length_in_pt($style->top, $cb["h"]); + //$right = $style->length_in_pt($style->right, $cb["w"]); + //$bottom = $style->length_in_pt($style->bottom, $cb["h"]); + $left = $style->length_in_pt($style->left, $cb["w"]); + + $x += $left; + $y += $top; + } + + $frame->set_position($x, $y); + } +} diff --git a/library/vendor/dompdf/src/Positioner/Fixed.php b/library/vendor/dompdf/src/Positioner/Fixed.php new file mode 100644 index 000000000..018f66c57 --- /dev/null +++ b/library/vendor/dompdf/src/Positioner/Fixed.php @@ -0,0 +1,95 @@ + + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\Positioner; + +use Dompdf\FrameDecorator\AbstractFrameDecorator; + +/** + * Positions fixely positioned frames + */ +class Fixed extends AbstractPositioner +{ + + function __construct(AbstractFrameDecorator $frame) + { + parent::__construct($frame); + } + + function position() + { + + $frame = $this->_frame; + $style = $frame->get_original_style(); + $root = $frame->get_root(); + $initialcb = $root->get_containing_block(); + $initialcb_style = $root->get_style(); + + $p = $frame->find_block_parent(); + if ($p) { + $p->add_line(); + } + + // Compute the margins of the @page style + $margin_top = $initialcb_style->length_in_pt($initialcb_style->margin_top, $initialcb["h"]); + $margin_right = $initialcb_style->length_in_pt($initialcb_style->margin_right, $initialcb["w"]); + $margin_bottom = $initialcb_style->length_in_pt($initialcb_style->margin_bottom, $initialcb["h"]); + $margin_left = $initialcb_style->length_in_pt($initialcb_style->margin_left, $initialcb["w"]); + + // The needed computed style of the element + $height = $style->length_in_pt($style->height, $initialcb["h"]); + $width = $style->length_in_pt($style->width, $initialcb["w"]); + + $top = $style->length_in_pt($style->top, $initialcb["h"]); + $right = $style->length_in_pt($style->right, $initialcb["w"]); + $bottom = $style->length_in_pt($style->bottom, $initialcb["h"]); + $left = $style->length_in_pt($style->left, $initialcb["w"]); + + $y = $margin_top; + if (isset($top)) { + $y = $top + $margin_top; + if ($top === "auto") { + $y = $margin_top; + if (isset($bottom) && $bottom !== "auto") { + $y = $initialcb["h"] - $bottom - $margin_bottom; + $margin_height = $this->_frame->get_margin_height(); + if ($margin_height !== "auto") { + $y -= $margin_height; + } else { + $y -= $height; + } + } + } + } + + $x = $margin_left; + if (isset($left)) { + $x = $left + $margin_left; + if ($left === "auto") { + $x = $margin_left; + if (isset($right) && $right !== "auto") { + $x = $initialcb["w"] - $right - $margin_right; + $margin_width = $this->_frame->get_margin_width(); + if ($margin_width !== "auto") { + $x -= $margin_width; + } else { + $x -= $width; + } + } + } + } + + $frame->set_position($x, $y); + + $children = $frame->get_children(); + foreach ($children as $child) { + $child->set_position($x, $y); + } + } +} \ No newline at end of file diff --git a/library/vendor/dompdf/src/Positioner/Inline.php b/library/vendor/dompdf/src/Positioner/Inline.php new file mode 100644 index 000000000..80eb677ba --- /dev/null +++ b/library/vendor/dompdf/src/Positioner/Inline.php @@ -0,0 +1,82 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\Positioner; + +use Dompdf\FrameDecorator\AbstractFrameDecorator; +use Dompdf\FrameDecorator\Inline as InlineFrameDecorator; +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\Exception; + +/** + * Positions inline frames + * + * @package dompdf + */ +class Inline extends AbstractPositioner +{ + + function __construct(AbstractFrameDecorator $frame) + { + parent::__construct($frame); + } + + //........................................................................ + + function position() + { + /** + * Find our nearest block level parent and access its lines property. + * @var BlockFrameDecorator + */ + $p = $this->_frame->find_block_parent(); + + // Debugging code: + +// Helpers::pre_r("\nPositioning:"); +// Helpers::pre_r("Me: " . $this->_frame->get_node()->nodeName . " (" . spl_object_hash($this->_frame->get_node()) . ")"); +// Helpers::pre_r("Parent: " . $p->get_node()->nodeName . " (" . spl_object_hash($p->get_node()) . ")"); + + // End debugging + + if (!$p) + throw new Exception("No block-level parent found. Not good."); + + $f = $this->_frame; + + $cb = $f->get_containing_block(); + $line = $p->get_current_line_box(); + + // Skip the page break if in a fixed position element + $is_fixed = false; + while ($f = $f->get_parent()) { + if ($f->get_style()->position === "fixed") { + $is_fixed = true; + break; + } + } + + $f = $this->_frame; + + if (!$is_fixed && $f->get_parent() && + $f->get_parent() instanceof InlineFrameDecorator && + $f->is_text_node() + ) { + + $min_max = $f->get_reflower()->get_min_max_width(); + + // If the frame doesn't fit in the current line, a line break occurs + if ($min_max["min"] > ($cb["w"] - $line->left - $line->w - $line->right)) { + $p->add_line(); + } + } + + $f->set_position($cb["x"] + $line->w, $line->y); + + } +} diff --git a/library/vendor/dompdf/src/Positioner/ListBullet.php b/library/vendor/dompdf/src/Positioner/ListBullet.php new file mode 100644 index 000000000..117c52182 --- /dev/null +++ b/library/vendor/dompdf/src/Positioner/ListBullet.php @@ -0,0 +1,81 @@ + + * @author Helmut Tischer + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\Positioner; + +use Dompdf\FrameDecorator\AbstractFrameDecorator; + +/** + * Positions list bullets + * + * @package dompdf + */ +class ListBullet extends AbstractPositioner +{ + + function __construct(AbstractFrameDecorator $frame) + { + parent::__construct($frame); + } + + //........................................................................ + + function position() + { + + // Bullets & friends are positioned an absolute distance to the left of + // the content edge of their parent element + $cb = $this->_frame->get_containing_block(); + + // Note: this differs from most frames in that we must position + // ourselves after determining our width + $x = $cb["x"] - $this->_frame->get_width(); + + $p = $this->_frame->find_block_parent(); + + $y = $p->get_current_line_box()->y; + + // This is a bit of a hack... + $n = $this->_frame->get_next_sibling(); + if ($n) { + $style = $n->get_style(); + $line_height = $style->length_in_pt($style->line_height, $style->get_font_size()); + $offset = $style->length_in_pt($line_height, $n->get_containing_block("h")) - $this->_frame->get_height(); + $y += $offset / 2; + } + + // Now the position is the left top of the block which should be marked with the bullet. + // We tried to find out the y of the start of the first text character within the block. + // But the top margin/padding does not fit, neither from this nor from the next sibling + // The "bit of a hack" above does not work also. + + // Instead let's position the bullet vertically centered to the block which should be marked. + // But for get_next_sibling() the get_containing_block is all zero, and for find_block_parent() + // the get_containing_block is paper width and the entire list as height. + + // if ($p) { + // //$cb = $n->get_containing_block(); + // $cb = $p->get_containing_block(); + // $y += $cb["h"]/2; + // print 'cb:'.$cb["x"].':'.$cb["y"].':'.$cb["w"].':'.$cb["h"].':'; + // } + + // Todo: + // For now give up on the above. Use Guesswork with font y-pos in the middle of the line spacing + + /*$style = $p->get_style(); + $font_size = $style->get_font_size(); + $line_height = $style->length_in_pt($style->line_height, $font_size); + $y += ($line_height - $font_size) / 2; */ + + //Position is x-end y-top of character position of the bullet. + $this->_frame->set_position($x, $y); + + } +} diff --git a/library/vendor/dompdf/src/Positioner/NullPositioner.php b/library/vendor/dompdf/src/Positioner/NullPositioner.php new file mode 100644 index 000000000..6b27f1949 --- /dev/null +++ b/library/vendor/dompdf/src/Positioner/NullPositioner.php @@ -0,0 +1,31 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\Positioner; + +use Dompdf\FrameDecorator\AbstractFrameDecorator; + +/** + * Dummy positioner + * + * @package dompdf + */ +class NullPositioner extends AbstractPositioner +{ + + function __construct(AbstractFrameDecorator $frame) + { + parent::__construct($frame); + } + + function position() + { + return; + } + +} diff --git a/library/vendor/dompdf/src/Positioner/TableCell.php b/library/vendor/dompdf/src/Positioner/TableCell.php new file mode 100644 index 000000000..1fda93d54 --- /dev/null +++ b/library/vendor/dompdf/src/Positioner/TableCell.php @@ -0,0 +1,37 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\Positioner; + +use Dompdf\FrameDecorator\AbstractFrameDecorator; +use Dompdf\FrameDecorator\Table; + +/** + * Positions table cells + * + * @package dompdf + */ +class TableCell extends AbstractPositioner +{ + + function __construct(AbstractFrameDecorator $frame) + { + parent::__construct($frame); + } + + //........................................................................ + + function position() + { + + $table = Table::find_parent_table($this->_frame); + $cellmap = $table->get_cellmap(); + $this->_frame->set_position($cellmap->get_frame_position($this->_frame)); + + } +} diff --git a/library/vendor/dompdf/src/Positioner/TableRow.php b/library/vendor/dompdf/src/Positioner/TableRow.php new file mode 100644 index 000000000..4dce49452 --- /dev/null +++ b/library/vendor/dompdf/src/Positioner/TableRow.php @@ -0,0 +1,43 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\Positioner; + +use Dompdf\FrameDecorator\AbstractFrameDecorator; + +/** + * Positions table rows + * + * @package dompdf + */ +class TableRow extends AbstractPositioner +{ + + function __construct(AbstractFrameDecorator $frame) + { + parent::__construct($frame); + } + + //........................................................................ + + function position() + { + + $cb = $this->_frame->get_containing_block(); + $p = $this->_frame->get_prev_sibling(); + + if ($p) + $y = $p->get_position("y") + $p->get_margin_height(); + + else + $y = $cb["y"]; + + $this->_frame->set_position($cb["x"], $y); + + } +} diff --git a/library/vendor/dompdf/src/Renderer.php b/library/vendor/dompdf/src/Renderer.php new file mode 100644 index 000000000..492a52364 --- /dev/null +++ b/library/vendor/dompdf/src/Renderer.php @@ -0,0 +1,296 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf; + +use Dompdf\Renderer\AbstractRenderer; +use Dompdf\Renderer\Block; +use Dompdf\Renderer\Image; +use Dompdf\Renderer\ListBullet; +use Dompdf\Renderer\TableCell; +use Dompdf\Renderer\TableRowGroup; +use Dompdf\Renderer\Text; + +use Dompdf\Frame; + +/** + * Concrete renderer + * + * Instantiates several specific renderers in order to render any given frame. + * + * @package dompdf + */ +class Renderer extends AbstractRenderer +{ + + /** + * Array of renderers for specific frame types + * + * @var AbstractRenderer[] + */ + protected $_renderers; + + /** + * Cache of the callbacks array + * + * @var array + */ + private $_callbacks; + + /** + * Advance the canvas to the next page + */ + function new_page() + { + $this->_canvas->new_page(); + } + + /** + * Render frames recursively + * + * @param Frame $frame the frame to render + */ + function render(Frame $frame) + { + global $_dompdf_debug; + + if ($_dompdf_debug) { + echo $frame; + flush(); + } + + $style = $frame->get_style(); + + if (in_array($style->visibility, array("hidden", "collapse"))) { + return; + } + + $display = $style->display; + + // Starts the CSS transformation + if ($style->transform && is_array($style->transform)) { + $this->_canvas->save(); + list($x, $y) = $frame->get_padding_box(); + $origin = $style->transform_origin; + + foreach ($style->transform as $transform) { + list($function, $values) = $transform; + if ($function === "matrix") { + $function = "transform"; + } + + $values = array_map("floatval", $values); + $values[] = $x + $style->length_in_pt($origin[0], $style->width); + $values[] = $y + $style->length_in_pt($origin[1], $style->height); + + call_user_func_array(array($this->_canvas, $function), $values); + } + } + + switch ($display) { + + case "block": + case "list-item": + case "inline-block": + case "table": + case "inline-table": + $this->_render_frame("block", $frame); + break; + + case "inline": + if ($frame->is_text_node()) { + $this->_render_frame("text", $frame); + } else { + $this->_render_frame("inline", $frame); + } + break; + + case "table-cell": + $this->_render_frame("table-cell", $frame); + break; + + case "table-row-group": + case "table-header-group": + case "table-footer-group": + $this->_render_frame("table-row-group", $frame); + break; + + case "-dompdf-list-bullet": + $this->_render_frame("list-bullet", $frame); + break; + + case "-dompdf-image": + $this->_render_frame("image", $frame); + break; + + case "none": + $node = $frame->get_node(); + + if ($node->nodeName === "script") { + if ($node->getAttribute("type") === "text/php" || + $node->getAttribute("language") === "php" + ) { + // Evaluate embedded php scripts + $this->_render_frame("php", $frame); + } elseif ($node->getAttribute("type") === "text/javascript" || + $node->getAttribute("language") === "javascript" + ) { + // Insert JavaScript + $this->_render_frame("javascript", $frame); + } + } + + // Don't render children, so skip to next iter + return; + + default: + break; + + } + + // Starts the overflow: hidden box + if ($style->overflow === "hidden") { + list($x, $y, $w, $h) = $frame->get_padding_box(); + + // get border radii + $style = $frame->get_style(); + list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h); + + if ($tl + $tr + $br + $bl > 0) { + $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl); + } else { + $this->_canvas->clipping_rectangle($x, $y, $w, $h); + } + } + + $stack = array(); + + foreach ($frame->get_children() as $child) { + // < 0 : nagative z-index + // = 0 : no z-index, no stacking context + // = 1 : stacking context without z-index + // > 1 : z-index + $child_style = $child->get_style(); + $child_z_index = $child_style->z_index; + $z_index = 0; + + if ($child_z_index !== "auto") { + $z_index = intval($child_z_index) + 1; + } elseif ($child_style->float !== "none" || $child->is_positionned()) { + $z_index = 1; + } + + $stack[$z_index][] = $child; + } + + ksort($stack); + + foreach ($stack as $by_index) { + foreach ($by_index as $child) { + $this->render($child); + } + } + + // Ends the overflow: hidden box + if ($style->overflow === "hidden") { + $this->_canvas->clipping_end(); + } + + if ($style->transform && is_array($style->transform)) { + $this->_canvas->restore(); + } + + // Check for end frame callback + $this->_check_callbacks("end_frame", $frame); + } + + /** + * Check for callbacks that need to be performed when a given event + * gets triggered on a frame + * + * @param string $event the type of event + * @param Frame $frame the frame that event is triggered on + */ + protected function _check_callbacks($event, $frame) + { + if (!isset($this->_callbacks)) { + $this->_callbacks = $this->_dompdf->get_callbacks(); + } + + if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) { + $info = array(0 => $this->_canvas, "canvas" => $this->_canvas, + 1 => $frame, "frame" => $frame); + $fs = $this->_callbacks[$event]; + foreach ($fs as $f) { + if (is_callable($f)) { + if (is_array($f)) { + $f[0]->{$f[1]}($info); + } else { + $f($info); + } + } + } + } + } + + /** + * Render a single frame + * + * Creates Renderer objects on demand + * + * @param string $type type of renderer to use + * @param Frame $frame the frame to render + */ + protected function _render_frame($type, $frame) + { + + if (!isset($this->_renderers[$type])) { + + switch ($type) { + case "block": + $this->_renderers[$type] = new Block($this->_dompdf); + break; + + case "inline": + $this->_renderers[$type] = new Renderer\Inline($this->_dompdf); + break; + + case "text": + $this->_renderers[$type] = new Text($this->_dompdf); + break; + + case "image": + $this->_renderers[$type] = new Image($this->_dompdf); + break; + + case "table-cell": + $this->_renderers[$type] = new TableCell($this->_dompdf); + break; + + case "table-row-group": + $this->_renderers[$type] = new TableRowGroup($this->_dompdf); + break; + + case "list-bullet": + $this->_renderers[$type] = new ListBullet($this->_dompdf); + break; + + case "php": + $this->_renderers[$type] = new PhpEvaluator($this->_canvas); + break; + + case "javascript": + $this->_renderers[$type] = new JavascriptEmbedder($this->_dompdf); + break; + + } + } + + $this->_renderers[$type]->render($frame); + + } +} diff --git a/library/vendor/dompdf/src/Renderer/AbstractRenderer.php b/library/vendor/dompdf/src/Renderer/AbstractRenderer.php new file mode 100644 index 000000000..31b595fd4 --- /dev/null +++ b/library/vendor/dompdf/src/Renderer/AbstractRenderer.php @@ -0,0 +1,768 @@ + + * @author Helmut Tischer + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Renderer; + +use Dompdf\Adapter\CPDF; +use Dompdf\Css\Color; +use Dompdf\Css\Style; +use Dompdf\Dompdf; +use Dompdf\Helpers; +use Dompdf\Frame; +use Dompdf\Image\Cache; + +/** + * Base renderer class + * + * @package dompdf + */ +abstract class AbstractRenderer +{ + + /** + * Rendering backend + * + * @var \Dompdf\Canvas + */ + protected $_canvas; + + /** + * Current dompdf instance + * + * @var Dompdf + */ + protected $_dompdf; + + /** + * Class constructor + * + * @param Dompdf $dompdf The current dompdf instance + */ + function __construct(Dompdf $dompdf) + { + $this->_dompdf = $dompdf; + $this->_canvas = $dompdf->get_canvas(); + } + + /** + * Render a frame. + * + * Specialized in child classes + * + * @param Frame $frame The frame to render + */ + abstract function render(Frame $frame); + + //........................................................................ + + /** + * Render a background image over a rectangular area + * + * @param string $url The background image to load + * @param float $x The left edge of the rectangular area + * @param float $y The top edge of the rectangular area + * @param float $width The width of the rectangular area + * @param float $height The height of the rectangular area + * @param Style $style The associated Style object + * + * @throws \Exception + */ + protected function _background_image($url, $x, $y, $width, $height, $style) + { + if (!function_exists("imagecreatetruecolor")) { + throw new \Exception("The PHP GD extension is required, but is not installed."); + } + + $sheet = $style->get_stylesheet(); + + // Skip degenerate cases + if ($width == 0 || $height == 0) { + return; + } + + $box_width = $width; + $box_height = $height; + + //debugpng + if ($this->_dompdf->get_option("debugPng")) print '[_background_image ' . $url . ']'; + + list($img, $type, /*$msg*/) = Cache::resolve_url( + $url, + $sheet->get_protocol(), + $sheet->get_host(), + $sheet->get_base_path(), + $this->_dompdf + ); + + // Bail if the image is no good + if (Cache::is_broken($img)) { + return; + } + + //Try to optimize away reading and composing of same background multiple times + //Postponing read with imagecreatefrom ...() + //final composition parameters and name not known yet + //Therefore read dimension directly from file, instead of creating gd object first. + //$img_w = imagesx($src); $img_h = imagesy($src); + + list($img_w, $img_h) = Helpers::dompdf_getimagesize($img, $this->_dompdf->getHttpContext()); + if (!isset($img_w) || $img_w == 0 || !isset($img_h) || $img_h == 0) { + return; + } + + $repeat = $style->background_repeat; + $dpi = $this->_dompdf->get_option("dpi"); + + //Increase background resolution and dependent box size according to image resolution to be placed in + //Then image can be copied in without resize + $bg_width = round((float)($width * $dpi) / 72); + $bg_height = round((float)($height * $dpi) / 72); + + //Need %bg_x, $bg_y as background pos, where img starts, converted to pixel + + list($bg_x, $bg_y) = $style->background_position; + + if (Helpers::is_percent($bg_x)) { + // The point $bg_x % from the left edge of the image is placed + // $bg_x % from the left edge of the background rectangle + $p = ((float)$bg_x) / 100.0; + $x1 = $p * $img_w; + $x2 = $p * $bg_width; + + $bg_x = $x2 - $x1; + } else { + $bg_x = (float)($style->length_in_pt($bg_x) * $dpi) / 72; + } + + $bg_x = round($bg_x + $style->length_in_pt($style->border_left_width) * $dpi / 72); + + if (Helpers::is_percent($bg_y)) { + // The point $bg_y % from the left edge of the image is placed + // $bg_y % from the left edge of the background rectangle + $p = ((float)$bg_y) / 100.0; + $y1 = $p * $img_h; + $y2 = $p * $bg_height; + + $bg_y = $y2 - $y1; + } else { + $bg_y = (float)($style->length_in_pt($bg_y) * $dpi) / 72; + } + + $bg_y = round($bg_y + $style->length_in_pt($style->border_top_width) * $dpi / 72); + + //clip background to the image area on partial repeat. Nothing to do if img off area + //On repeat, normalize start position to the tile at immediate left/top or 0/0 of area + //On no repeat with positive offset: move size/start to have offset==0 + //Handle x/y Dimensions separately + + if ($repeat !== "repeat" && $repeat !== "repeat-x") { + //No repeat x + if ($bg_x < 0) { + $bg_width = $img_w + $bg_x; + } else { + $x += ($bg_x * 72) / $dpi; + $bg_width = $bg_width - $bg_x; + if ($bg_width > $img_w) { + $bg_width = $img_w; + } + $bg_x = 0; + } + + if ($bg_width <= 0) { + return; + } + + $width = (float)($bg_width * 72) / $dpi; + } else { + //repeat x + if ($bg_x < 0) { + $bg_x = -((-$bg_x) % $img_w); + } else { + $bg_x = $bg_x % $img_w; + if ($bg_x > 0) { + $bg_x -= $img_w; + } + } + } + + if ($repeat !== "repeat" && $repeat !== "repeat-y") { + //no repeat y + if ($bg_y < 0) { + $bg_height = $img_h + $bg_y; + } else { + $y += ($bg_y * 72) / $dpi; + $bg_height = $bg_height - $bg_y; + if ($bg_height > $img_h) { + $bg_height = $img_h; + } + $bg_y = 0; + } + if ($bg_height <= 0) { + return; + } + $height = (float)($bg_height * 72) / $dpi; + } else { + //repeat y + if ($bg_y < 0) { + $bg_y = -((-$bg_y) % $img_h); + } else { + $bg_y = $bg_y % $img_h; + if ($bg_y > 0) { + $bg_y -= $img_h; + } + } + } + + //Optimization, if repeat has no effect + if ($repeat === "repeat" && $bg_y <= 0 && $img_h + $bg_y >= $bg_height) { + $repeat = "repeat-x"; + } + + if ($repeat === "repeat" && $bg_x <= 0 && $img_w + $bg_x >= $bg_width) { + $repeat = "repeat-y"; + } + + if (($repeat === "repeat-x" && $bg_x <= 0 && $img_w + $bg_x >= $bg_width) || + ($repeat === "repeat-y" && $bg_y <= 0 && $img_h + $bg_y >= $bg_height) + ) { + $repeat = "no-repeat"; + } + + //Use filename as indicator only + //different names for different variants to have different copies in the pdf + //This is not dependent of background color of box! .'_'.(is_array($bg_color) ? $bg_color["hex"] : $bg_color) + //Note: Here, bg_* are the start values, not end values after going through the tile loops! + + $filedummy = $img; + + $is_png = false; + $filedummy .= '_' . $bg_width . '_' . $bg_height . '_' . $bg_x . '_' . $bg_y . '_' . $repeat; + + //Optimization to avoid multiple times rendering the same image. + //If check functions are existing and identical image already cached, + //then skip creation of duplicate, because it is not needed by addImagePng + if ($this->_canvas instanceof CPDF && + $this->_canvas->get_cpdf()->image_iscached($filedummy) + ) { + $bg = null; + } else { + + // Create a new image to fit over the background rectangle + $bg = imagecreatetruecolor($bg_width, $bg_height); + + switch (strtolower($type)) { + case "png": + $is_png = true; + imagesavealpha($bg, true); + imagealphablending($bg, false); + $src = imagecreatefrompng($img); + break; + + case "jpeg": + $src = imagecreatefromjpeg($img); + break; + + case "gif": + $src = imagecreatefromgif($img); + break; + + case "bmp": + $src = Helpers::imagecreatefrombmp($img); + break; + + default: + return; // Unsupported image type + } + + if ($src == null) { + return; + } + + //Background color if box is not relevant here + //Non transparent image: box clipped to real size. Background non relevant. + //Transparent image: The image controls the transparency and lets shine through whatever background. + //However on transparent image preset the composed image with the transparency color, + //to keep the transparency when copying over the non transparent parts of the tiles. + $ti = imagecolortransparent($src); + + if ($ti >= 0) { + $tc = imagecolorsforindex($src, $ti); + $ti = imagecolorallocate($bg, $tc['red'], $tc['green'], $tc['blue']); + imagefill($bg, 0, 0, $ti); + imagecolortransparent($bg, $ti); + } + + //This has only an effect for the non repeatable dimension. + //compute start of src and dest coordinates of the single copy + if ($bg_x < 0) { + $dst_x = 0; + $src_x = -$bg_x; + } else { + $src_x = 0; + $dst_x = $bg_x; + } + + if ($bg_y < 0) { + $dst_y = 0; + $src_y = -$bg_y; + } else { + $src_y = 0; + $dst_y = $bg_y; + } + + //For historical reasons exchange meanings of variables: + //start_* will be the start values, while bg_* will be the temporary start values in the loops + $start_x = $bg_x; + $start_y = $bg_y; + + // Copy regions from the source image to the background + if ($repeat === "no-repeat") { + + // Simply place the image on the background + imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $img_h); + + } else if ($repeat === "repeat-x") { + + for ($bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w) { + if ($bg_x < 0) { + $dst_x = 0; + $src_x = -$bg_x; + $w = $img_w + $bg_x; + } else { + $dst_x = $bg_x; + $src_x = 0; + $w = $img_w; + } + imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $img_h); + } + + } else if ($repeat === "repeat-y") { + + for ($bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h) { + if ($bg_y < 0) { + $dst_y = 0; + $src_y = -$bg_y; + $h = $img_h + $bg_y; + } else { + $dst_y = $bg_y; + $src_y = 0; + $h = $img_h; + } + imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $h); + + } + + } else if ($repeat === "repeat") { + + for ($bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h) { + for ($bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w) { + + if ($bg_x < 0) { + $dst_x = 0; + $src_x = -$bg_x; + $w = $img_w + $bg_x; + } else { + $dst_x = $bg_x; + $src_x = 0; + $w = $img_w; + } + + if ($bg_y < 0) { + $dst_y = 0; + $src_y = -$bg_y; + $h = $img_h + $bg_y; + } else { + $dst_y = $bg_y; + $src_y = 0; + $h = $img_h; + } + imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $h); + } + } + } else { + print 'Unknown repeat!'; + } + + imagedestroy($src); + + } /* End optimize away creation of duplicates */ + + $this->_canvas->clipping_rectangle($x, $y, $box_width, $box_height); + + //img: image url string + //img_w, img_h: original image size in px + //width, height: box size in pt + //bg_width, bg_height: box size in px + //x, y: left/top edge of box on page in pt + //start_x, start_y: placement of image relative to pattern + //$repeat: repeat mode + //$bg: GD object of result image + //$src: GD object of original image + //When using cpdf and optimization to direct png creation from gd object is available, + //don't create temp file, but place gd object directly into the pdf + if (!$is_png && $this->_canvas instanceof CPDF) { + // Note: CPDF_Adapter image converts y position + $this->_canvas->get_cpdf()->addImagePng($filedummy, $x, $this->_canvas->get_height() - $y - $height, $width, $height, $bg); + } else { + $tmp_dir = $this->_dompdf->get_option("temp_dir"); + $tmp_name = tempnam($tmp_dir, "bg_dompdf_img_"); + @unlink($tmp_name); + $tmp_file = "$tmp_name.png"; + + //debugpng + if ($this->_dompdf->get_option("debugPng")) print '[_background_image ' . $tmp_file . ']'; + + imagepng($bg, $tmp_file); + $this->_canvas->image($tmp_file, $x, $y, $width, $height); + imagedestroy($bg); + + //debugpng + if ($this->_dompdf->get_option("debugPng")) print '[_background_image unlink ' . $tmp_file . ']'; + + if (!$this->_dompdf->get_option("debugKeepTemp")) { + unlink($tmp_file); + } + } + + $this->_canvas->clipping_end(); + } + + protected function _get_dash_pattern($style, $width) + { + $pattern = array(); + + switch ($style) { + default: + /*case "solid": + case "double": + case "groove": + case "inset": + case "outset": + case "ridge":*/ + case "none": + break; + + case "dotted": + if ($width <= 1) + $pattern = array($width, $width * 2); + else + $pattern = array($width); + break; + + case "dashed": + $pattern = array(3 * $width); + break; + } + + return $pattern; + } + + protected function _border_none($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + return; + } + + protected function _border_hidden($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + return; + } + + // Border rendering functions + + protected function _border_dotted($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dotted", $r1, $r2); + } + + + protected function _border_dashed($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dashed", $r1, $r2); + } + + + protected function _border_solid($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + // TODO: Solve rendering where one corner is beveled (radius == 0), one corner isn't. + if ($corner_style !== "bevel" || $r1 > 0 || $r2 > 0) { + // do it the simple way + $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "solid", $r1, $r2); + return; + } + + list($top, $right, $bottom, $left) = $widths; + + // All this polygon business is for beveled corners... + switch ($side) { + case "top": + $points = array($x, $y, + $x + $length, $y, + $x + $length - $right, $y + $top, + $x + $left, $y + $top); + $this->_canvas->polygon($points, $color, null, null, true); + break; + + case "bottom": + $points = array($x, $y, + $x + $length, $y, + $x + $length - $right, $y - $bottom, + $x + $left, $y - $bottom); + $this->_canvas->polygon($points, $color, null, null, true); + break; + + case "left": + $points = array($x, $y, + $x, $y + $length, + $x + $left, $y + $length - $bottom, + $x + $left, $y + $top); + $this->_canvas->polygon($points, $color, null, null, true); + break; + + case "right": + $points = array($x, $y, + $x, $y + $length, + $x - $right, $y + $length - $bottom, + $x - $right, $y + $top); + $this->_canvas->polygon($points, $color, null, null, true); + break; + + default: + return; + } + } + + protected function _apply_ratio($side, $ratio, $top, $right, $bottom, $left, &$x, &$y, &$length, &$r1, &$r2) + { + switch ($side) { + + case "top": + $r1 -= $left * $ratio; + $r2 -= $right * $ratio; + $x += $left * $ratio; + $y += $top * $ratio; + $length -= $left * $ratio + $right * $ratio; + break; + + case "bottom": + $r1 -= $right * $ratio; + $r2 -= $left * $ratio; + $x += $left * $ratio; + $y -= $bottom * $ratio; + $length -= $left * $ratio + $right * $ratio; + break; + + case "left": + $r1 -= $top * $ratio; + $r2 -= $bottom * $ratio; + $x += $left * $ratio; + $y += $top * $ratio; + $length -= $top * $ratio + $bottom * $ratio; + break; + + case "right": + $r1 -= $bottom * $ratio; + $r2 -= $top * $ratio; + $x -= $right * $ratio; + $y += $top * $ratio; + $length -= $top * $ratio + $bottom * $ratio; + break; + + default: + return; + + } + } + + protected function _border_double($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + list($top, $right, $bottom, $left) = $widths; + + $third_widths = array($top / 3, $right / 3, $bottom / 3, $left / 3); + + // draw the outer border + $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2); + + $this->_apply_ratio($side, 2 / 3, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2); + + $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2); + } + + protected function _border_groove($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + list($top, $right, $bottom, $left) = $widths; + + $half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2); + + $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); + + $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2); + + $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); + + } + + protected function _border_ridge($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + list($top, $right, $bottom, $left) = $widths; + + $half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2); + + $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); + + $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2); + + $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); + + } + + protected function _tint($c) + { + if (!is_numeric($c)) + return $c; + + return min(1, $c + 0.16); + } + + protected function _shade($c) + { + if (!is_numeric($c)) + return $c; + + return max(0, $c - 0.33); + } + + protected function _border_inset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + switch ($side) { + case "top": + case "left": + $shade = array_map(array($this, "_shade"), $color); + $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2); + break; + + case "bottom": + case "right": + $tint = array_map(array($this, "_tint"), $color); + $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2); + break; + + default: + return; + } + } + + protected function _border_outset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + switch ($side) { + case "top": + case "left": + $tint = array_map(array($this, "_tint"), $color); + $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2); + break; + + case "bottom": + case "right": + $shade = array_map(array($this, "_shade"), $color); + $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2); + break; + + default: + return; + } + } + + // Draws a solid, dotted, or dashed line, observing the border radius + protected function _border_line($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $pattern_name, $r1 = 0, $r2 = 0) + { + list($top, $right, $bottom, $left) = $widths; + + $width = $$side; + $pattern = $this->_get_dash_pattern($pattern_name, $width); + + $half_width = $width / 2; + $r1 -= $half_width; + $r2 -= $half_width; + $adjust = $r1 / 80; + $length -= $width; + + switch ($side) { + case "top": + $x += $half_width; + $y += $half_width; + + if ($r1 > 0) { + $this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 90 - $adjust, 135 + $adjust, $color, $width, $pattern); + } + + $this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern); + + if ($r2 > 0) { + $this->_canvas->arc($x + $length - $r2, $y + $r2, $r2, $r2, 45 - $adjust, 90 + $adjust, $color, $width, $pattern); + } + break; + + case "bottom": + $x += $half_width; + $y -= $half_width; + + if ($r1 > 0) { + $this->_canvas->arc($x + $r1, $y - $r1, $r1, $r1, 225 - $adjust, 270 + $adjust, $color, $width, $pattern); + } + + $this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern); + + if ($r2 > 0) { + $this->_canvas->arc($x + $length - $r2, $y - $r2, $r2, $r2, 270 - $adjust, 315 + $adjust, $color, $width, $pattern); + } + break; + + case "left": + $y += $half_width; + $x += $half_width; + + if ($r1 > 0) { + $this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 135 - $adjust, 180 + $adjust, $color, $width, $pattern); + } + + $this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern); + + if ($r2 > 0) { + $this->_canvas->arc($x + $r2, $y + $length - $r2, $r2, $r2, 180 - $adjust, 225 + $adjust, $color, $width, $pattern); + } + break; + + case "right": + $y += $half_width; + $x -= $half_width; + + if ($r1 > 0) { + $this->_canvas->arc($x - $r1, $y + $r1, $r1, $r1, 0 - $adjust, 45 + $adjust, $color, $width, $pattern); + } + + $this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern); + + if ($r2 > 0) { + $this->_canvas->arc($x - $r2, $y + $length - $r2, $r2, $r2, 315 - $adjust, 360 + $adjust, $color, $width, $pattern); + } + break; + } + } + + protected function _set_opacity($opacity) + { + if (is_numeric($opacity) && $opacity <= 1.0 && $opacity >= 0.0) { + $this->_canvas->set_opacity($opacity); + } + } + + protected function _debug_layout($box, $color = "red", $style = array()) + { + $this->_canvas->rectangle($box[0], $box[1], $box[2], $box[3], Color::parse($color), 0.1, $style); + } +} diff --git a/library/vendor/dompdf/src/Renderer/Block.php b/library/vendor/dompdf/src/Renderer/Block.php new file mode 100644 index 000000000..d16212307 --- /dev/null +++ b/library/vendor/dompdf/src/Renderer/Block.php @@ -0,0 +1,240 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Renderer; + +use Dompdf\Frame; +use Dompdf\FrameDecorator\AbstractFrameDecorator; +use Dompdf\Helpers; + +/** + * Renders block frames + * + * @package dompdf + */ +class Block extends AbstractRenderer +{ + + //........................................................................ + + function render(Frame $frame) + { + $style = $frame->get_style(); + $node = $frame->get_node(); + + list($x, $y, $w, $h) = $frame->get_border_box(); + + $this->_set_opacity($frame->get_opacity($style->opacity)); + + if ($node->nodeName === "body") { + $h = $frame->get_containing_block("h") - $style->length_in_pt(array( + $style->margin_top, + $style->border_top_width, + $style->border_bottom_width, + $style->margin_bottom), + $style->width); + } + + // Handle anchors & links + if ($node->nodeName === "a" && $href = $node->getAttribute("href")) { + $href = Helpers::build_url($this->_dompdf->getProtocol(), $this->_dompdf->getBaseHost(), $this->_dompdf->getBasePath(), $href); + $this->_canvas->add_link($href, $x, $y, $w, $h); + } + + // Draw our background, border and content + list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h); + + if ($tl + $tr + $br + $bl > 0) { + $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl); + } + + if (($bg = $style->background_color) !== "transparent") { + $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg); + } + + if (($url = $style->background_image) && $url !== "none") { + $this->_background_image($url, $x, $y, $w, $h, $style); + } + + if ($tl + $tr + $br + $bl > 0) { + $this->_canvas->clipping_end(); + } + + $border_box = array($x, $y, $w, $h); + $this->_render_border($frame, $border_box); + $this->_render_outline($frame, $border_box); + + if ($this->_dompdf->get_option("debugLayout") && $this->_dompdf->get_option("debugLayoutBlocks")) { + $this->_debug_layout($frame->get_border_box(), "red"); + if ($this->_dompdf->get_option("debugLayoutPaddingBox")) { + $this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5)); + } + } + + if ($this->_dompdf->get_option("debugLayout") && $this->_dompdf->get_option("debugLayoutLines") && $frame->get_decorator()) { + foreach ($frame->get_decorator()->get_line_boxes() as $line) { + $frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange"); + } + } + } + + protected function _render_border(AbstractFrameDecorator $frame, $border_box = null, $corner_style = "bevel") + { + $style = $frame->get_style(); + $bp = $style->get_border_properties(); + + if (empty($border_box)) { + $border_box = $frame->get_border_box(); + } + + // find the radius + $radius = $style->get_computed_border_radius($border_box[2], $border_box[3]); // w, h + + // Short-cut: If all the borders are "solid" with the same color and style, and no radius, we'd better draw a rectangle + if ( + in_array($bp["top"]["style"], array("solid", "dashed", "dotted")) && + $bp["top"] == $bp["right"] && + $bp["right"] == $bp["bottom"] && + $bp["bottom"] == $bp["left"] && + array_sum($radius) == 0 + ) { + $props = $bp["top"]; + if ($props["color"] === "transparent" || $props["width"] <= 0) return; + + list($x, $y, $w, $h) = $border_box; + $width = $style->length_in_pt($props["width"]); + $pattern = $this->_get_dash_pattern($props["style"], $width); + $this->_canvas->rectangle($x + $width / 2, $y + $width / 2, $w - $width, $h - $width, $props["color"], $width, $pattern); + return; + } + + // Do it the long way + $widths = array($style->length_in_pt($bp["top"]["width"]), + $style->length_in_pt($bp["right"]["width"]), + $style->length_in_pt($bp["bottom"]["width"]), + $style->length_in_pt($bp["left"]["width"])); + + foreach ($bp as $side => $props) { + list($x, $y, $w, $h) = $border_box; + $length = 0; + $r1 = 0; + $r2 = 0; + + if (!$props["style"] || + $props["style"] === "none" || + $props["width"] <= 0 || + $props["color"] == "transparent" + ) + continue; + + switch ($side) { + case "top": + $length = $w; + $r1 = $radius["top-left"]; + $r2 = $radius["top-right"]; + break; + + case "bottom": + $length = $w; + $y += $h; + $r1 = $radius["bottom-left"]; + $r2 = $radius["bottom-right"]; + break; + + case "left": + $length = $h; + $r1 = $radius["top-left"]; + $r2 = $radius["bottom-left"]; + break; + + case "right": + $length = $h; + $x += $w; + $r1 = $radius["top-right"]; + $r2 = $radius["bottom-right"]; + break; + default: + break; + } + $method = "_border_" . $props["style"]; + + // draw rounded corners + $this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style, $r1, $r2); + } + } + + protected function _render_outline(AbstractFrameDecorator $frame, $border_box = null, $corner_style = "bevel") + { + $style = $frame->get_style(); + + $props = array( + "width" => $style->outline_width, + "style" => $style->outline_style, + "color" => $style->outline_color, + ); + + if (!$props["style"] || $props["style"] === "none" || $props["width"] <= 0) + return; + + if (empty($border_box)) { + $border_box = $frame->get_border_box(); + } + + $offset = $style->length_in_pt($props["width"]); + $pattern = $this->_get_dash_pattern($props["style"], $offset); + + // If the outline style is "solid" we'd better draw a rectangle + if (in_array($props["style"], array("solid", "dashed", "dotted"))) { + $border_box[0] -= $offset / 2; + $border_box[1] -= $offset / 2; + $border_box[2] += $offset; + $border_box[3] += $offset; + + list($x, $y, $w, $h) = $border_box; + $this->_canvas->rectangle($x, $y, $w, $h, $props["color"], $offset, $pattern); + return; + } + + $border_box[0] -= $offset; + $border_box[1] -= $offset; + $border_box[2] += $offset * 2; + $border_box[3] += $offset * 2; + + $method = "_border_" . $props["style"]; + $widths = array_fill(0, 4, $props["width"]); + $sides = array("top", "right", "left", "bottom"); + $length = 0; + + foreach ($sides as $side) { + list($x, $y, $w, $h) = $border_box; + + switch ($side) { + case "top": + $length = $w; + break; + + case "bottom": + $length = $w; + $y += $h; + break; + + case "left": + $length = $h; + break; + + case "right": + $length = $h; + $x += $w; + break; + default: + break; + } + + $this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style); + } + } +} diff --git a/library/vendor/dompdf/src/Renderer/Image.php b/library/vendor/dompdf/src/Renderer/Image.php new file mode 100644 index 000000000..1630794a4 --- /dev/null +++ b/library/vendor/dompdf/src/Renderer/Image.php @@ -0,0 +1,125 @@ + + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Renderer; + +use Dompdf\Frame; +use Dompdf\Image\Cache; + +/** + * Image renderer + * + * @access private + * @package dompdf + */ +class Image extends Block +{ + + function render(Frame $frame) + { + // Render background & borders + $style = $frame->get_style(); + $cb = $frame->get_containing_block(); + list($x, $y, $w, $h) = $frame->get_border_box(); + + $this->_set_opacity($frame->get_opacity($style->opacity)); + + list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h); + + $has_border_radius = $tl + $tr + $br + $bl > 0; + + if ($has_border_radius) { + $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl); + } + + if (($bg = $style->background_color) !== "transparent") { + $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg); + } + + if (($url = $style->background_image) && $url !== "none") { + $this->_background_image($url, $x, $y, $w, $h, $style); + } + + if ($has_border_radius) { + $this->_canvas->clipping_end(); + } + + $this->_render_border($frame); + $this->_render_outline($frame); + + list($x, $y) = $frame->get_padding_box(); + + $x += $style->length_in_pt($style->padding_left, $cb["w"]); + $y += $style->length_in_pt($style->padding_top, $cb["h"]); + + $w = $style->length_in_pt($style->width, $cb["w"]); + $h = $style->length_in_pt($style->height, $cb["h"]); + + if ($has_border_radius) { + list($wt, $wr, $wb, $wl) = array( + $style->border_top_width, + $style->border_right_width, + $style->border_bottom_width, + $style->border_left_width, + ); + + // we have to get the "inner" radius + if ($tl > 0) { + $tl -= ($wt + $wl) / 2; + } + if ($tr > 0) { + $tr -= ($wt + $wr) / 2; + } + if ($br > 0) { + $br -= ($wb + $wr) / 2; + } + if ($bl > 0) { + $bl -= ($wb + $wl) / 2; + } + + $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl); + } + + $src = $frame->get_image_url(); + $alt = null; + + if (Cache::is_broken($src) && + $alt = $frame->get_node()->getAttribute("alt") + ) { + $font = $style->font_family; + $size = $style->font_size; + $spacing = $style->word_spacing; + $this->_canvas->text($x, $y, $alt, + $font, $size, + $style->color, $spacing); + } else { + $this->_canvas->image($src, $x, $y, $w, $h, $style->image_resolution); + } + + if ($has_border_radius) { + $this->_canvas->clipping_end(); + } + + if ($msg = $frame->get_image_msg()) { + $parts = preg_split("/\s*\n\s*/", $msg); + $height = 10; + $_y = $alt ? $y + $h - count($parts) * $height : $y; + + foreach ($parts as $i => $_part) { + $this->_canvas->text($x, $_y + $i * $height, $_part, "times", $height * 0.8, array(0.5, 0.5, 0.5)); + } + } + + if ($this->_dompdf->get_option("debugLayout") && $this->_dompdf->get_option("debugLayoutBlocks")) { + $this->_debug_layout($frame->get_border_box(), "blue"); + if ($this->_dompdf->get_option("debugLayoutPaddingBox")) { + $this->_debug_layout($frame->get_padding_box(), "blue", array(0.5, 0.5)); + } + } + } +} diff --git a/library/vendor/dompdf/src/Renderer/Inline.php b/library/vendor/dompdf/src/Renderer/Inline.php new file mode 100644 index 000000000..a7c8fd107 --- /dev/null +++ b/library/vendor/dompdf/src/Renderer/Inline.php @@ -0,0 +1,198 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Renderer; + +use Dompdf\Frame; +use Dompdf\Helpers; + +/** + * Renders inline frames + * + * @access private + * @package dompdf + */ +class Inline extends AbstractRenderer +{ + + //........................................................................ + + function render(Frame $frame) + { + $style = $frame->get_style(); + + if (!$frame->get_first_child()) + return; // No children, no service + + // Draw the left border if applicable + $bp = $style->get_border_properties(); + $widths = array($style->length_in_pt($bp["top"]["width"]), + $style->length_in_pt($bp["right"]["width"]), + $style->length_in_pt($bp["bottom"]["width"]), + $style->length_in_pt($bp["left"]["width"])); + + // Draw the background & border behind each child. To do this we need + // to figure out just how much space each child takes: + list($x, $y) = $frame->get_first_child()->get_position(); + $w = null; + $h = 0; +// $x += $widths[3]; +// $y += $widths[0]; + + $this->_set_opacity($frame->get_opacity($style->opacity)); + + $first_row = true; + + foreach ($frame->get_children() as $child) { + list($child_x, $child_y, $child_w, $child_h) = $child->get_padding_box(); + + if (!is_null($w) && $child_x < $x + $w) { + //This branch seems to be supposed to being called on the first part + //of an inline html element, and the part after the if clause for the + //parts after a line break. + //But because $w initially mostly is 0, and gets updated only on the next + //round, this seem to be never executed and the common close always. + + // The next child is on another line. Draw the background & + // borders on this line. + + // Background: + if (($bg = $style->background_color) !== "transparent") + $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg); + + if (($url = $style->background_image) && $url !== "none") { + $this->_background_image($url, $x, $y, $w, $h, $style); + } + + // If this is the first row, draw the left border + if ($first_row) { + + if ($bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $bp["left"]["width"] > 0) { + $method = "_border_" . $bp["left"]["style"]; + $this->$method($x, $y, $h + $widths[0] + $widths[2], $bp["left"]["color"], $widths, "left"); + } + $first_row = false; + } + + // Draw the top & bottom borders + if ($bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $bp["top"]["width"] > 0) { + $method = "_border_" . $bp["top"]["style"]; + $this->$method($x, $y, $w + $widths[1] + $widths[3], $bp["top"]["color"], $widths, "top"); + } + + if ($bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $bp["bottom"]["width"] > 0) { + $method = "_border_" . $bp["bottom"]["style"]; + $this->$method($x, $y + $h + $widths[0] + $widths[2], $w + $widths[1] + $widths[3], $bp["bottom"]["color"], $widths, "bottom"); + } + + // Handle anchors & links + $link_node = null; + if ($frame->get_node()->nodeName === "a") { + $link_node = $frame->get_node(); + } else if ($frame->get_parent()->get_node()->nodeName === "a") { + $link_node = $frame->get_parent()->get_node(); + } + + if ($link_node && $href = $link_node->getAttribute("href")) { + $href = Helpers::build_url($this->_dompdf->getProtocol(), $this->_dompdf->getBaseHost(), $this->_dompdf->getBasePath(), $href); + $this->_canvas->add_link($href, $x, $y, $w, $h); + } + + $x = $child_x; + $y = $child_y; + $w = $child_w; + $h = $child_h; + continue; + } + + if (is_null($w)) + $w = $child_w; + else + $w += $child_w; + + $h = max($h, $child_h); + + if ($this->_dompdf->get_option("debugLayout") && $this->_dompdf->get_option("debugLayoutInline")) { + $this->_debug_layout($child->get_border_box(), "blue"); + if ($this->_dompdf->get_option("debugLayoutPaddingBox")) { + $this->_debug_layout($child->get_padding_box(), "blue", array(0.5, 0.5)); + } + } + } + + + // Handle the last child + if (($bg = $style->background_color) !== "transparent") + $this->_canvas->filled_rectangle($x + $widths[3], $y + $widths[0], $w, $h, $bg); + + //On continuation lines (after line break) of inline elements, the style got copied. + //But a non repeatable background image should not be repeated on the next line. + //But removing the background image above has never an effect, and removing it below + //removes it always, even on the initial line. + //Need to handle it elsewhere, e.g. on certain ...clone()... usages. + // Repeat not given: default is Style::__construct + // ... && (!($repeat = $style->background_repeat) || $repeat === "repeat" ... + //different position? $this->_background_image($url, $x, $y, $w, $h, $style); + if (($url = $style->background_image) && $url !== "none") + $this->_background_image($url, $x + $widths[3], $y + $widths[0], $w, $h, $style); + + // Add the border widths + $w += $widths[1] + $widths[3]; + $h += $widths[0] + $widths[2]; + + // make sure the border and background start inside the left margin + $left_margin = $style->length_in_pt($style->margin_left); + $x += $left_margin; + + // If this is the first row, draw the left border too + if ($first_row && $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $widths[3] > 0) { + $method = "_border_" . $bp["left"]["style"]; + $this->$method($x, $y, $h, $bp["left"]["color"], $widths, "left"); + } + + // Draw the top & bottom borders + if ($bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $widths[0] > 0) { + $method = "_border_" . $bp["top"]["style"]; + $this->$method($x, $y, $w, $bp["top"]["color"], $widths, "top"); + } + + if ($bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $widths[2] > 0) { + $method = "_border_" . $bp["bottom"]["style"]; + $this->$method($x, $y + $h, $w, $bp["bottom"]["color"], $widths, "bottom"); + } + + // Helpers::var_dump(get_class($frame->get_next_sibling())); + // $last_row = get_class($frame->get_next_sibling()) !== 'Inline'; + // Draw the right border if this is the last row + if ($bp["right"]["style"] !== "none" && $bp["right"]["color"] !== "transparent" && $widths[1] > 0) { + $method = "_border_" . $bp["right"]["style"]; + $this->$method($x + $w, $y, $h, $bp["right"]["color"], $widths, "right"); + } + + // Only two levels of links frames + $link_node = null; + if ($frame->get_node()->nodeName === "a") { + $link_node = $frame->get_node(); + + if (($name = $link_node->getAttribute("name")) || ($name = $link_node->getAttribute("id"))) { + $this->_canvas->add_named_dest($name); + } + } + + if ($frame->get_parent() && $frame->get_parent()->get_node()->nodeName === "a") { + $link_node = $frame->get_parent()->get_node(); + } + + // Handle anchors & links + if ($link_node) { + if ($href = $link_node->getAttribute("href")) { + $href = Helpers::build_url($this->_dompdf->getProtocol(), $this->_dompdf->getBaseHost(), $this->_dompdf->getBasePath(), $href); + $this->_canvas->add_link($href, $x, $y, $w, $h); + } + } + } +} diff --git a/library/vendor/dompdf/src/Renderer/ListBullet.php b/library/vendor/dompdf/src/Renderer/ListBullet.php new file mode 100644 index 000000000..c7fcc89b5 --- /dev/null +++ b/library/vendor/dompdf/src/Renderer/ListBullet.php @@ -0,0 +1,248 @@ + + * @author Helmut Tischer + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Renderer; + +use Dompdf\Helpers; +use Dompdf\FontMetrics; +use Dompdf\Frame; +use Dompdf\Image\Cache; +use Dompdf\FrameDecorator\ListBullet as ListBulletFrameDecorator; + +/** + * Renders list bullets + * + * @access private + * @package dompdf + */ +class ListBullet extends AbstractRenderer +{ + static function get_counter_chars($type) + { + static $cache = array(); + + if (isset($cache[$type])) { + return $cache[$type]; + } + + $uppercase = false; + $text = ""; + + switch ($type) { + case "decimal-leading-zero": + case "decimal": + case "1": + return "0123456789"; + + case "upper-alpha": + case "upper-latin": + case "A": + $uppercase = true; + case "lower-alpha": + case "lower-latin": + case "a": + $text = "abcdefghijklmnopqrstuvwxyz"; + break; + + case "upper-roman": + case "I": + $uppercase = true; + case "lower-roman": + case "i": + $text = "ivxlcdm"; + break; + + case "lower-greek": + for ($i = 0; $i < 24; $i++) { + $text .= Helpers::unichr($i + 944); + } + break; + } + + if ($uppercase) { + $text = strtoupper($text); + } + + return $cache[$type] = "$text."; + } + + /** + * @param integer $n + * @param string $type + * @param integer $pad + * + * @return string + */ + private function make_counter($n, $type, $pad = null) + { + $n = intval($n); + $text = ""; + $uppercase = false; + + switch ($type) { + case "decimal-leading-zero": + case "decimal": + case "1": + if ($pad) + $text = str_pad($n, $pad, "0", STR_PAD_LEFT); + else + $text = $n; + break; + + case "upper-alpha": + case "upper-latin": + case "A": + $uppercase = true; + case "lower-alpha": + case "lower-latin": + case "a": + $text = chr(($n % 26) + ord('a') - 1); + break; + + case "upper-roman": + case "I": + $uppercase = true; + case "lower-roman": + case "i": + $text = Helpers::dec2roman($n); + break; + + case "lower-greek": + $text = Helpers::unichr($n + 944); + break; + } + + if ($uppercase) { + $text = strtoupper($text); + } + + return "$text."; + } + + function render(Frame $frame) + { + $style = $frame->get_style(); + $font_size = $style->get_font_size(); + $line_height = $style->length_in_pt($style->line_height, $frame->get_containing_block("w")); + + $this->_set_opacity($frame->get_opacity($style->opacity)); + + $li = $frame->get_parent(); + + // Don't render bullets twice if if was split + if ($li->_splitted) { + return; + } + + // Handle list-style-image + // If list style image is requested but missing, fall back to predefined types + if ($style->list_style_image !== "none" && + !Cache::is_broken($img = $frame->get_image_url()) + ) { + + list($x, $y) = $frame->get_position(); + + //For expected size and aspect, instead of box size, use image natural size scaled to DPI. + // Resample the bullet image to be consistent with 'auto' sized images + // See also Image::get_min_max_width + // Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary. + //$w = $frame->get_width(); + //$h = $frame->get_height(); + list($width, $height) = Helpers::dompdf_getimagesize($img, $this->_dompdf->getHttpContext()); + $dpi = $this->_dompdf->get_option("dpi"); + $w = ((float)rtrim($width, "px") * 72) / $dpi; + $h = ((float)rtrim($height, "px") * 72) / $dpi; + + $x -= $w; + $y -= ($line_height - $font_size) / 2; //Reverse hinting of list_bullet_positioner + + $this->_canvas->image($img, $x, $y, $w, $h); + + } else { + + $bullet_style = $style->list_style_type; + + $fill = false; + + switch ($bullet_style) { + + default: + case "disc": + $fill = true; + + case "circle": + list($x, $y) = $frame->get_position(); + $r = ($font_size * (ListBulletFrameDecorator::BULLET_SIZE /*-ListBulletFrameDecorator::BULLET_THICKNESS*/)) / 2; + $x -= $font_size * (ListBulletFrameDecorator::BULLET_SIZE / 2); + $y += ($font_size * (1 - ListBulletFrameDecorator::BULLET_DESCENT)) / 2; + $o = $font_size * ListBulletFrameDecorator::BULLET_THICKNESS; + $this->_canvas->circle($x, $y, $r, $style->color, $o, null, $fill); + break; + + case "square": + list($x, $y) = $frame->get_position(); + $w = $font_size * ListBulletFrameDecorator::BULLET_SIZE; + $x -= $w; + $y += ($font_size * (1 - ListBulletFrameDecorator::BULLET_DESCENT - ListBulletFrameDecorator::BULLET_SIZE)) / 2; + $this->_canvas->filled_rectangle($x, $y, $w, $w, $style->color); + break; + + case "decimal-leading-zero": + case "decimal": + case "lower-alpha": + case "lower-latin": + case "lower-roman": + case "lower-greek": + case "upper-alpha": + case "upper-latin": + case "upper-roman": + case "1": // HTML 4.0 compatibility + case "a": + case "i": + case "A": + case "I": + $pad = null; + if ($bullet_style === "decimal-leading-zero") { + $pad = strlen($li->get_parent()->get_node()->getAttribute("dompdf-children-count")); + } + + $node = $frame->get_node(); + + if (!$node->hasAttribute("dompdf-counter")) { + return; + } + + $index = $node->getAttribute("dompdf-counter"); + $text = $this->make_counter($index, $bullet_style, $pad); + + if (trim($text) == "") { + return; + } + + $spacing = 0; + $font_family = $style->font_family; + + $line = $li->get_containing_line(); + list($x, $y) = array($frame->get_position("x"), $line->y); + + $x -= $this->_dompdf->getFontMetrics()->getTextWidth($text, $font_family, $font_size, $spacing); + + // Take line-height into account + $line_height = $style->line_height; + $y += ($line_height - $font_size) / 4; // FIXME I thought it should be 2, but 4 gives better results + + $this->_canvas->text($x, $y, $text, + $font_family, $font_size, + $style->color, $spacing); + + case "none": + break; + } + } + } +} diff --git a/library/vendor/dompdf/src/Renderer/TableCell.php b/library/vendor/dompdf/src/Renderer/TableCell.php new file mode 100644 index 000000000..541b9cd7c --- /dev/null +++ b/library/vendor/dompdf/src/Renderer/TableCell.php @@ -0,0 +1,160 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Renderer; + +use Dompdf\Frame; +use Dompdf\FrameDecorator\Table; + +/** + * Renders table cells + * + * @package dompdf + */ +class TableCell extends Block +{ + + //........................................................................ + + function render(Frame $frame) + { + $style = $frame->get_style(); + + if (trim($frame->get_node()->nodeValue) === "" && $style->empty_cells === "hide") { + return; + } + + $this->_set_opacity($frame->get_opacity($style->opacity)); + list($x, $y, $w, $h) = $frame->get_border_box(); + + // Draw our background, border and content + if (($bg = $style->background_color) !== "transparent") { + $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg); + } + + if (($url = $style->background_image) && $url !== "none") { + $this->_background_image($url, $x, $y, $w, $h, $style); + } + + $table = Table::find_parent_table($frame); + + if ($table->get_style()->border_collapse !== "collapse") { + $this->_render_border($frame); + $this->_render_outline($frame); + return; + } + + // The collapsed case is slightly complicated... + // @todo Add support for outlines here + + $cellmap = $table->get_cellmap(); + $cells = $cellmap->get_spanned_cells($frame); + $num_rows = $cellmap->get_num_rows(); + $num_cols = $cellmap->get_num_cols(); + + // Determine the top row spanned by this cell + $i = $cells["rows"][0]; + $top_row = $cellmap->get_row($i); + + // Determine if this cell borders on the bottom of the table. If so, + // then we draw its bottom border. Otherwise the next row down will + // draw its top border instead. + if (in_array($num_rows - 1, $cells["rows"])) { + $draw_bottom = true; + $bottom_row = $cellmap->get_row($num_rows - 1); + } else + $draw_bottom = false; + + + // Draw the horizontal borders + foreach ($cells["columns"] as $j) { + $bp = $cellmap->get_border_properties($i, $j); + + $y = $top_row["y"] - $bp["top"]["width"] / 2; + + $col = $cellmap->get_column($j); + $x = $col["x"] - $bp["left"]["width"] / 2; + $w = $col["used-width"] + ($bp["left"]["width"] + $bp["right"]["width"]) / 2; + + if ($bp["top"]["style"] !== "none" && $bp["top"]["width"] > 0) { + $widths = array($bp["top"]["width"], + $bp["right"]["width"], + $bp["bottom"]["width"], + $bp["left"]["width"]); + $method = "_border_" . $bp["top"]["style"]; + $this->$method($x, $y, $w, $bp["top"]["color"], $widths, "top", "square"); + } + + if ($draw_bottom) { + $bp = $cellmap->get_border_properties($num_rows - 1, $j); + if ($bp["bottom"]["style"] === "none" || $bp["bottom"]["width"] <= 0) + continue; + + $y = $bottom_row["y"] + $bottom_row["height"] + $bp["bottom"]["width"] / 2; + + $widths = array($bp["top"]["width"], + $bp["right"]["width"], + $bp["bottom"]["width"], + $bp["left"]["width"]); + $method = "_border_" . $bp["bottom"]["style"]; + $this->$method($x, $y, $w, $bp["bottom"]["color"], $widths, "bottom", "square"); + + } + } + + $j = $cells["columns"][0]; + + $left_col = $cellmap->get_column($j); + + if (in_array($num_cols - 1, $cells["columns"])) { + $draw_right = true; + $right_col = $cellmap->get_column($num_cols - 1); + } else + $draw_right = false; + + // Draw the vertical borders + foreach ($cells["rows"] as $i) { + $bp = $cellmap->get_border_properties($i, $j); + + $x = $left_col["x"] - $bp["left"]["width"] / 2; + + $row = $cellmap->get_row($i); + + $y = $row["y"] - $bp["top"]["width"] / 2; + $h = $row["height"] + ($bp["top"]["width"] + $bp["bottom"]["width"]) / 2; + + if ($bp["left"]["style"] !== "none" && $bp["left"]["width"] > 0) { + + $widths = array($bp["top"]["width"], + $bp["right"]["width"], + $bp["bottom"]["width"], + $bp["left"]["width"]); + + $method = "_border_" . $bp["left"]["style"]; + $this->$method($x, $y, $h, $bp["left"]["color"], $widths, "left", "square"); + } + + if ($draw_right) { + $bp = $cellmap->get_border_properties($i, $num_cols - 1); + if ($bp["right"]["style"] === "none" || $bp["right"]["width"] <= 0) + continue; + + $x = $right_col["x"] + $right_col["used-width"] + $bp["right"]["width"] / 2; + + $widths = array($bp["top"]["width"], + $bp["right"]["width"], + $bp["bottom"]["width"], + $bp["left"]["width"]); + + $method = "_border_" . $bp["right"]["style"]; + $this->$method($x, $y, $h, $bp["right"]["color"], $widths, "right", "square"); + + } + } + + } +} diff --git a/library/vendor/dompdf/src/Renderer/TableRowGroup.php b/library/vendor/dompdf/src/Renderer/TableRowGroup.php new file mode 100644 index 000000000..9b29babfd --- /dev/null +++ b/library/vendor/dompdf/src/Renderer/TableRowGroup.php @@ -0,0 +1,44 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Renderer; + +use Dompdf\Frame; + +/** + * Renders block frames + * + * @package dompdf + */ +class TableRowGroup extends Block +{ + + //........................................................................ + + function render(Frame $frame) + { + $style = $frame->get_style(); + + $this->_set_opacity($frame->get_opacity($style->opacity)); + + $this->_render_border($frame); + $this->_render_outline($frame); + + if ($this->_dompdf->get_option("debugLayout") && $this->_dompdf->get_option("debugLayoutBlocks")) { + $this->_debug_layout($frame->get_border_box(), "red"); + if ($this->_dompdf->get_option("debugLayoutPaddingBox")) { + $this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5)); + } + } + + if ($this->_dompdf->get_option("debugLayout") && $this->_dompdf->get_option("debugLayoutLines") && $frame->get_decorator()) { + foreach ($frame->get_decorator()->get_line_boxes() as $line) { + $frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange"); + } + } + } +} diff --git a/library/vendor/dompdf/src/Renderer/Text.php b/library/vendor/dompdf/src/Renderer/Text.php new file mode 100644 index 000000000..3072a1792 --- /dev/null +++ b/library/vendor/dompdf/src/Renderer/Text.php @@ -0,0 +1,158 @@ + + * @author Helmut Tischer + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Renderer; + +use Dompdf\Adapter\CPDF; +use Dompdf\FontMetrics; +use Dompdf\Frame; + +/** + * Renders text frames + * + * @package dompdf + */ +class Text extends AbstractRenderer +{ + + const DECO_THICKNESS = 0.02; // Thickness of underline. Screen: 0.08, print: better less, e.g. 0.04 + + //Tweaking if $base and $descent are not accurate. + //Check method_exists( $this->_canvas, "get_cpdf" ) + //- For cpdf these can and must stay 0, because font metrics are used directly. + //- For other renderers, if different values are wanted, separate the parameter sets. + // But $size and $size-$height seem to be accurate enough + const UNDERLINE_OFFSET = 0.0; // Relative to bottom of text, as fraction of height. + const OVERLINE_OFFSET = 0.0; // Relative to top of text + const LINETHROUGH_OFFSET = 0.0; // Relative to centre of text. + const DECO_EXTENSION = 0.0; // How far to extend lines past either end, in pt + + //........................................................................ + + /** + * @param \Dompdf\FrameDecorator\Text $frame + */ + function render(Frame $frame) + { + $text = $frame->get_text(); + if (trim($text) === "") + return; + + $style = $frame->get_style(); + list($x, $y) = $frame->get_position(); + $cb = $frame->get_containing_block(); + + if (($ml = $style->margin_left) === "auto" || $ml === "none") + $ml = 0; + + if (($pl = $style->padding_left) === "auto" || $pl === "none") + $pl = 0; + + if (($bl = $style->border_left_width) === "auto" || $bl === "none") + $bl = 0; + + $x += $style->length_in_pt(array($ml, $pl, $bl), $cb["w"]); + + $font = $style->font_family; + $size = $frame_font_size = $style->font_size; + $height = $style->height; + $word_spacing = $frame->get_text_spacing() + $style->length_in_pt($style->word_spacing); + $char_spacing = $style->length_in_pt($style->letter_spacing); + $width = $style->width; + + /*$text = str_replace( + array("{PAGE_NUM}"), + array($this->_canvas->get_page_number()), + $text + );*/ + + $this->_canvas->text($x, $y, $text, + $font, $size, + $style->color, $word_spacing, $char_spacing); + + $line = $frame->get_containing_line(); + + // FIXME Instead of using the tallest frame to position, + // the decoration, the text should be well placed + if (false && $line->tallest_frame) { + $base_frame = $line->tallest_frame; + $style = $base_frame->get_style(); + $size = $style->font_size; + $height = $line->h * ($size / $style->line_height); + } + + $line_thickness = $size * self::DECO_THICKNESS; + $underline_offset = $size * self::UNDERLINE_OFFSET; + $overline_offset = $size * self::OVERLINE_OFFSET; + $linethrough_offset = $size * self::LINETHROUGH_OFFSET; + $underline_position = -0.08; + + if ($this->_canvas instanceof CPDF) { + $cpdf_font = $this->_canvas->get_cpdf()->fonts[$style->font_family]; + + if (isset($cpdf_font["UnderlinePosition"])) { + $underline_position = $cpdf_font["UnderlinePosition"] / 1000; + } + + if (isset($cpdf_font["UnderlineThickness"])) { + $line_thickness = $size * ($cpdf_font["UnderlineThickness"] / 1000); + } + } + + $descent = $size * $underline_position; + $base = $size; + + // Handle text decoration: + // http://www.w3.org/TR/CSS21/text.html#propdef-text-decoration + + // Draw all applicable text-decorations. Start with the root and work our way down. + $p = $frame; + $stack = array(); + while ($p = $p->get_parent()) + $stack[] = $p; + + while (isset($stack[0])) { + $f = array_pop($stack); + + if (($text_deco = $f->get_style()->text_decoration) === "none") + continue; + + $deco_y = $y; //$line->y; + $color = $f->get_style()->color; + + switch ($text_deco) { + + default: + continue; + + case "underline": + $deco_y += $base - $descent + $underline_offset + $line_thickness / 2; + break; + + case "overline": + $deco_y += $overline_offset + $line_thickness / 2; + break; + + case "line-through": + $deco_y += $base * 0.7 + $linethrough_offset; + break; + } + + $dx = 0; + $x1 = $x - self::DECO_EXTENSION; + $x2 = $x + $width + $dx + self::DECO_EXTENSION; + $this->_canvas->line($x1, $deco_y, $x2, $deco_y, $color, $line_thickness); + } + + if ($this->_dompdf->get_option("debugLayout") && $this->_dompdf->get_option("debugLayoutLines")) { + $text_width = $this->_dompdf->getFontMetrics()->getTextWidth($text, $font, $frame_font_size); + $this->_debug_layout(array($x, $y, $text_width + ($line->wc - 1) * $word_spacing, $frame_font_size), "orange", array(0.5, 0.5)); + } + } +} diff --git a/modules/doc/application/controllers/ModuleController.php b/modules/doc/application/controllers/ModuleController.php index fafd94656..030e90465 100644 --- a/modules/doc/application/controllers/ModuleController.php +++ b/modules/doc/application/controllers/ModuleController.php @@ -91,7 +91,7 @@ class ModuleController extends DocController $module = $this->params->getRequired('moduleName'); $this->assertModuleInstalled($module); $moduleManager = Icinga::app()->getModuleManager(); - $name = $moduleManager->getModule($module)->getTitle(); + $name = $moduleManager->getModule($module, false)->getTitle(); try { $this->renderToc( $this->getPath($module, Icinga::app()->getModuleManager()->getModuleDir($module, '/doc')), diff --git a/modules/doc/public/img/doc/markdown.png b/modules/doc/doc/img/markdown.png similarity index 100% rename from modules/doc/public/img/doc/markdown.png rename to modules/doc/doc/img/markdown.png diff --git a/modules/doc/module.info b/modules/doc/module.info index e69a16605..10d96a7cf 100644 --- a/modules/doc/module.info +++ b/modules/doc/module.info @@ -1,4 +1,4 @@ Module: doc -Version: 2.3.2 +Version: 2.3.4 Description: Documentation module Extracts, shows and exports documentation for Icinga Web 2 and its modules. diff --git a/modules/monitoring/application/controllers/ActionsController.php b/modules/monitoring/application/controllers/ActionsController.php index 06bbc8e61..1ec5fe102 100644 --- a/modules/monitoring/application/controllers/ActionsController.php +++ b/modules/monitoring/application/controllers/ActionsController.php @@ -61,7 +61,7 @@ class Monitoring_ActionsController extends Controller $filter = $this->getFilterOrExitIfEmpty(); $downtimes = $this->backend ->select() - ->from('downtime', array('host_name', 'id' => 'downtime_internal_id')) + ->from('downtime', array('host_name', 'id' => 'downtime_internal_id', 'name' => 'downtime_name')) ->where('object_type', 'host') ->applyFilter($this->getRestriction('monitoring/filter/objects')) ->applyFilter($filter); @@ -110,7 +110,10 @@ class Monitoring_ActionsController extends Controller $filter = $this->getFilterOrExitIfEmpty(); $downtimes = $this->backend ->select() - ->from('downtime', array('host_name', 'service_description', 'id' => 'downtime_internal_id')) + ->from( + 'downtime', + array('host_name', 'service_description', 'id' => 'downtime_internal_id', 'name' => 'downtime_name') + ) ->where('object_type', 'service') ->applyFilter($this->getRestriction('monitoring/filter/objects')) ->applyFilter($filter); diff --git a/modules/monitoring/application/controllers/ChartController.php b/modules/monitoring/application/controllers/ChartController.php deleted file mode 100644 index a92a8bcdb..000000000 --- a/modules/monitoring/application/controllers/ChartController.php +++ /dev/null @@ -1,405 +0,0 @@ -alignTopLeft(); - $chart->setAxisLabel('X axis label', 'Y axis label') - ->setYAxis(new LogarithmicUnit()); - - for ($i = -15; $i < 15; $i++) { - $data1[] = array($i, -$i * rand(1, 10) * pow(2, rand(1, 2))); - } - for ($i = -15; $i < 15; $i++) { - $data2[] = array($i, 1000 + $i * rand(1, 35) * pow(2, rand(1, 2))); - } - for ($i = -15; $i < 15; $i++) { - $data3[] = array($i, $i * rand(1, 100) * pow(2, rand(1, 10)) - 1000); - } - - $chart->drawLines( - array( - 'label' => 'Random 1', - 'color' => '#F56', - 'data' => $data1, - 'showPoints' => true - ) - ); - $chart->drawLines( - array( - 'label' => 'Random 2', - 'color' => '#fa4', - 'data' => $data2, - 'showPoints' => true - ) - ); - $chart->drawLines( - array( - 'label' => 'Random 3', - 'color' => '#4b7', - 'data' => $data3, - 'showPoints' => true - ) - ); - return $chart; - } - - private function drawLogChart2() - { - $chart = new GridChart(); - $chart->alignTopLeft(); - $chart->setAxisLabel('X axis label', 'Y axis label') - ->setYAxis(new LogarithmicUnit()); - - for ($i = -10; $i < 10; $i++) { - $sign = $i > 0 ? 1 : - ($i < 0 ? -1 : 0); - $data[] = array($i, $sign * pow(10, abs($i))); - } - $chart->drawLines( - array( - 'label' => 'f(x): sign(x) * 10^|x|', - 'color' => '#F56', - 'data' => $data, - 'showPoints' => true - ) - ); - return $chart; - } - private function drawLogChart3() - { - $chart = new GridChart(); - $chart->alignTopLeft(); - $chart->setAxisLabel('X axis label', 'Y axis label') - ->setYAxis(new LogarithmicUnit()); - - for ($i = -2; $i < 3; $i++) { - $sign = $i > 0 ? 1 : - ($i < 0 ? -1 : 0); - $data[] = array($i, $sign * pow(10, abs($i))); - } - $chart->drawLines( - array( - 'label' => 'f(x): sign(x) * 10^|x|', - 'color' => '#F56', - 'data' => $data, - 'showPoints' => true - ) - ); - return $chart; - } - - public function testAction() - { - $this->chart = new GridChart(); - $this->chart->alignTopLeft(); - $this->chart->setAxisLabel('X axis label', 'Y axis label')->setXAxis(new StaticAxis()); - $data1 = array(); - $data2 = array(); - $data3 = array(); - for ($i = 0; $i < 50; $i++) { - $data3[] = array('Label ' . $i, rand(0, 30)); - } - - /* - $this->chart->drawLines( - array( - 'label' => 'Nr of outtakes', - 'color' => '#F56', - 'width' => '5', - - 'data' => $data - ), array( - 'label' => 'Some line', - 'color' => '#fa4', - 'width' => '4', - - 'data' => $data3, - 'showPoints' => true - ) - ); -*/ - $this->chart->drawBars( - array( - 'label' => 'A big amount of data', - 'color' => '#4b7', - 'data' => $data3, - 'showPoints' => true - ) - ); -/* - $this->chart->drawLines( - array( - 'label' => 'Nr of outtakes', - 'color' => 'yellow', - 'width' => '5', - 'data' => $data2 - ) - ); -*/ - $this->view->svgs = array(); - $this->view->svgs[] = $this->drawLogChart1(); - $this->view->svgs[] = $this->drawLogChart2(); - $this->view->svgs[] = $this->drawLogChart3(); - $this->view->svgs[] = $this->chart; - } - - public function hostgroupAction() - { - $query = $this->backend->select()->from( - 'hostgroupsummary', - array( - 'hostgroup', - 'hosts_up', - 'hosts_unreachable_handled', - 'hosts_unreachable_unhandled', - 'hosts_down_handled', - 'hosts_down_unhandled', - 'hosts_pending', - 'services_ok', - 'services_unknown_handled', - 'services_unknown_unhandled', - 'services_critical_handled', - 'services_critical_unhandled', - 'services_warning_handled', - 'services_warning_unhandled', - 'services_pending' - ) - )->order('hostgroup')->getQuery()->fetchAll(); - $this->view->height = intval($this->getParam('height', 500)); - $this->view->width = intval($this->getParam('width', 500)); - if (count($query) === 1) { - $this->drawHostGroupPie($query[0]); - } else { - $this->drawHostGroupChart($query); - } - } - - public function servicegroupAction() - { - $query = $this->backend->select()->from( - 'servicegroupsummary', - array( - 'servicegroup', - 'services_ok', - 'services_unknown_handled', - 'services_unknown_unhandled', - 'services_critical_handled', - 'services_critical_unhandled', - 'services_warning_handled', - 'services_warning_unhandled', - 'services_pending' - ) - )->order('servicegroup')->getQuery()->fetchAll(); - $this->view->height = intval($this->getParam('height', 500)); - $this->view->width = intval($this->getParam('width', 500)); - - - if (count($query) === 1) { - $this->drawServiceGroupPie($query[0]); - } else { - $this->drawServiceGroupChart($query); - } - } - - private function drawServiceGroupChart($query) - { - $okBars = array(); - $warningBars = array(); - $critBars = array(); - $unknownBars = array(); - foreach ($query as $servicegroup) { - $okBars[] = array($servicegroup->servicegroup, $servicegroup->services_ok); - $warningBars[] = array($servicegroup->servicegroup, $servicegroup->services_warning_unhandled); - $critBars[] = array($servicegroup->servicegroup, $servicegroup->services_critical_unhandled); - $unknownBars[] = array($servicegroup->servicegroup, $servicegroup->services_unknown_unhandled); - } - $this->view->chart = new GridChart(); - $this->view->chart->title = $this->translate('Service Group Chart'); - $this->view->chart->description = $this->translate('Contains service states for each service group.'); - - $this->view->chart->alignTopLeft(); - $this->view->chart->setAxisLabel('', $this->translate('Services')) - ->setXAxis(new StaticAxis()) - ->setAxisMin(null, 0); - - $tooltip = $this->translate('{title}:
{value} of {sum} services are {label}'); - $this->view->chart->drawBars( - array( - 'label' => $this->translate('Ok'), - 'color' => '#44bb77', - 'stack' => 'stack1', - 'data' => $okBars, - 'tooltip' => $tooltip - ), - array( - 'label' => $this->translate('Warning'), - 'color' => '#ffaa44', - 'stack' => 'stack1', - 'data' => $warningBars, - 'tooltip' => $tooltip - ), - array( - 'label' => $this->translate('Critical'), - 'color' => '#ff5566', - 'stack' => 'stack1', - 'data' => $critBars, - 'tooltip' => $tooltip - ), - array( - 'label' => $this->translate('Unknown'), - 'color' => '#dd66ff', - 'stack' => 'stack1', - 'data' => $unknownBars, - 'tooltip' => $tooltip - ) - ); - } - - private function drawHostGroupChart($query) - { - $upBars = array(); - $downBars = array(); - $unreachableBars = array(); - for ($i = 0; $i < 50; $i++) { - $upBars[] = array( - (string)$i, rand(1, 200), rand(1, 200) - ); - $downBars[] = array( - (string)$i, rand(1, 200), rand(1, 200) - ); - $unreachableBars[] = array( - (string)$i, rand(1, 200), rand(1, 200) - ); - } - $tooltip = $this->translate('{title}:
{value} of {sum} hosts are {label}'); - $this->view->chart = new GridChart(); - $this->view->chart->title = $this->translate('Host Group Chart'); - $this->view->chart->description = $this->translate('Contains host states of each service group.'); - - $this->view->chart->alignTopLeft(); - $this->view->chart->setAxisLabel('', $this->translate('Hosts')) - ->setXAxis(new StaticAxis()) - ->setAxisMin(null, 0); - $this->view->chart->drawBars( - array( - 'label' => $this->translate('Up'), - 'color' => '#44bb77', - 'stack' => 'stack1', - 'data' => $upBars, - 'tooltip' => $tooltip - ), - array( - 'label' => $this->translate('Down'), - 'color' => '#ff5566', - 'stack' => 'stack1', - 'data' => $downBars, - 'tooltip' => $tooltip - ), - array( - 'label' => $this->translate('Unreachable'), - 'color' => '#dd66ff', - 'stack' => 'stack1', - 'data' => $unreachableBars, - 'tooltip' => $tooltip - ) - ); - } - - private function drawServiceGroupPie($query) - { - $this->view->chart = new PieChart(); - $this->view->chart->alignTopLeft(); - $this->view->chart->drawPie(array( - 'data' => array( - (int) $query->services_ok, - (int) $query->services_warning_unhandled, - (int) $query->services_warning_handled, - (int) $query->services_critical_unhandled, - (int) $query->services_critical_handled, - (int) $query->services_unknown_unhandled, - (int) $query->services_unknown_handled, - (int) $query->services_pending - ), - 'colors' => array('#44bb77', '#ff4444', '#ff0000', '#ffff00', '#ffff33', '#E066FF', '#f099FF', '#fefefe'), - 'labels'=> array( - $query->services_ok . ' Up Services', - $query->services_warning_handled . $this->translate(' Warning Services (Handled)'), - $query->services_warning_unhandled . $this->translate(' Warning Services (Unhandled)'), - $query->services_critical_handled . $this->translate(' Down Services (Handled)'), - $query->services_critical_unhandled . $this->translate(' Down Services (Unhandled)'), - $query->services_unknown_handled . $this->translate(' Unreachable Services (Handled)'), - $query->services_unknown_unhandled . $this->translate(' Unreachable Services (Unhandled)'), - $query->services_pending . $this->translate(' Pending Services') - ) - )); - } - - private function drawHostGroupPie($query) - { - $this->view->chart = new PieChart(); - $this->view->chart->alignTopLeft(); - $this->view->chart->drawPie(array( - 'data' => array( - (int) $query->hosts_up, - (int) $query->hosts_down_handled, - (int) $query->hosts_down_unhandled, - (int) $query->hosts_unreachable_handled, - (int) $query->hosts_unreachable_unhandled, - (int) $query->hosts_pending - ), - 'colors' => array( - '#44bb77', // 'Ok' - '#ff4444', // 'Warning' - '#ff0000', // 'WarningHandled' - '#E066FF', - '#f099FF', - '#fefefe' - ), - 'labels'=> array( - (int) $query->hosts_up . $this->translate(' Up Hosts'), - (int) $query->hosts_down_handled . $this->translate(' Down Hosts (Handled)'), - (int) $query->hosts_down_unhandled . $this->translate(' Down Hosts (Unhandled)'), - (int) $query->hosts_unreachable_handled . $this->translate(' Unreachable Hosts (Handled)'), - (int) $query->hosts_unreachable_unhandled . $this->translate(' Unreachable Hosts (Unhandled)'), - (int) $query->hosts_pending . $this->translate(' Pending Hosts') - ) - ), array( - 'data' => array( - (int) $query->services_ok, - (int) $query->services_warning_unhandled, - (int) $query->services_warning_handled, - (int) $query->services_critical_unhandled, - (int) $query->services_critical_handled, - (int) $query->services_unknown_unhandled, - (int) $query->services_unknown_handled, - (int) $query->services_pending - ), - 'colors' => array('#44bb77', '#ff4444', '#ff0000', '#ffff00', '#ffff33', '#E066FF', '#f099FF', '#fefefe'), - 'labels'=> array( - $query->services_ok . $this->translate(' Up Services'), - $query->services_warning_handled . $this->translate(' Warning Services (Handled)'), - $query->services_warning_unhandled . $this->translate(' Warning Services (Unhandled)'), - $query->services_critical_handled . $this->translate(' Down Services (Handled)'), - $query->services_critical_unhandled . $this->translate(' Down Services (Unhandled)'), - $query->services_unknown_handled . $this->translate(' Unreachable Services (Handled)'), - $query->services_unknown_unhandled . $this->translate(' Unreachable Services (Unhandled)'), - $query->services_pending . $this->translate(' Pending Services') - ) - )); - } -} diff --git a/modules/monitoring/application/controllers/CommentController.php b/modules/monitoring/application/controllers/CommentController.php index 122927531..4e085c5b9 100644 --- a/modules/monitoring/application/controllers/CommentController.php +++ b/modules/monitoring/application/controllers/CommentController.php @@ -37,6 +37,7 @@ class CommentController extends Controller 'type' => 'comment_type', 'persistent' => 'comment_is_persistent', 'expiration' => 'comment_expiration', + 'name' => 'comment_name', 'host_name', 'service_description', 'host_display_name', @@ -51,7 +52,7 @@ class CommentController extends Controller $this->getTabs()->add( 'comment', array( - 'icon' => 'comment', + 'icon' => 'comment-empty', 'label' => $this->translate('Comment'), 'title' => $this->translate('Display detailed information about a comment.'), 'url' =>'monitoring/comments/show' @@ -73,6 +74,7 @@ class CommentController extends Controller ->populate(array( 'comment_id' => $this->comment->id, 'comment_is_service' => isset($this->comment->service_description), + 'comment_name' => $this->comment->name, 'redirect' => $listUrl )) ->handleRequest(); diff --git a/modules/monitoring/application/controllers/CommentsController.php b/modules/monitoring/application/controllers/CommentsController.php index ac118c477..6dcf08ec4 100644 --- a/modules/monitoring/application/controllers/CommentsController.php +++ b/modules/monitoring/application/controllers/CommentsController.php @@ -46,6 +46,7 @@ class CommentsController extends Controller 'type' => 'comment_type', 'persistent' => 'comment_is_persistent', 'expiration' => 'comment_expiration', + 'name' => 'comment_name', 'host_name', 'service_description', 'host_display_name', @@ -58,7 +59,7 @@ class CommentsController extends Controller $this->getTabs()->add( 'comments', array( - 'icon' => 'comment', + 'icon' => 'comment-empty', 'label' => $this->translate('Comments') . sprintf(' (%d)', $query->count()), 'title' => $this->translate( 'Display detailed information about multiple comments.' diff --git a/modules/monitoring/application/controllers/ConfigController.php b/modules/monitoring/application/controllers/ConfigController.php index 09aeb7d30..33ddaeacf 100644 --- a/modules/monitoring/application/controllers/ConfigController.php +++ b/modules/monitoring/application/controllers/ConfigController.php @@ -106,7 +106,7 @@ class ConfigController extends Controller $form->setOnSuccess(function (BackendConfigForm $form) { try { - $form->add(array_filter($form->getValues())); + $form->add($form::transformEmptyValuesToNull($form->getValues())); } catch (Exception $e) { $form->error($e->getMessage()); return false; @@ -258,7 +258,7 @@ class ConfigController extends Controller ); $form->setOnSuccess(function (TransportConfigForm $form) { try { - $form->add(array_filter($form->getValues())); + $form->add($form::transformEmptyValuesToNull($form->getValues())); } catch (Exception $e) { $form->error($e->getMessage()); return false; diff --git a/modules/monitoring/application/controllers/DowntimeController.php b/modules/monitoring/application/controllers/DowntimeController.php index 6e8a19809..e19ce38c0 100644 --- a/modules/monitoring/application/controllers/DowntimeController.php +++ b/modules/monitoring/application/controllers/DowntimeController.php @@ -44,6 +44,7 @@ class DowntimeController extends Controller 'is_fixed' => 'downtime_is_fixed', 'is_in_effect' => 'downtime_is_in_effect', 'entry_time' => 'downtime_entry_time', + 'name' => 'downtime_name', 'host_state', 'service_state', 'host_name', @@ -91,6 +92,7 @@ class DowntimeController extends Controller ->populate(array( 'downtime_id' => $this->downtime->id, 'downtime_is_service' => $isService, + 'downtime_name' => $this->downtime->name, 'redirect' => Url::fromPath('monitoring/list/downtimes'), )) ->handleRequest(); diff --git a/modules/monitoring/application/controllers/DowntimesController.php b/modules/monitoring/application/controllers/DowntimesController.php index fb15c98de..0b33aa339 100644 --- a/modules/monitoring/application/controllers/DowntimesController.php +++ b/modules/monitoring/application/controllers/DowntimesController.php @@ -51,6 +51,7 @@ class DowntimesController extends Controller 'is_fixed' => 'downtime_is_fixed', 'is_in_effect' => 'downtime_is_in_effect', 'entry_time' => 'downtime_entry_time', + 'name' => 'downtime_name', 'host_state', 'service_state', 'host_name', diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index 8e493ed44..cfbff2169 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -229,6 +229,7 @@ class ListController extends Controller 'is_fixed' => 'downtime_is_fixed', 'is_in_effect' => 'downtime_is_in_effect', 'entry_time' => 'downtime_entry_time', + 'name' => 'downtime_name', 'host_state', 'service_state', 'host_name', @@ -276,13 +277,13 @@ class ListController extends Controller $this->setAutorefreshInterval(15); $notifications = $this->backend->select()->from('notification', array( - 'host_name', - 'service_description', - 'notification_output', - 'notification_contact_name', - 'notification_start_time', - 'notification_state', 'host_display_name', + 'host_name', + 'notification_contact_name', + 'notification_output', + 'notification_state', + 'notification_timestamp', + 'service_description', 'service_display_name' )); $this->applyRestriction('monitoring/filter/objects', $notifications); @@ -291,7 +292,7 @@ class ListController extends Controller $this->setupPaginationControl($notifications); $this->setupLimitControl(); $this->setupSortControl(array( - 'notification_start_time' => $this->translate('Notification Start') + 'notification_timestamp' => $this->translate('Notification Start') ), $notifications); $this->view->notifications = $notifications; @@ -422,6 +423,7 @@ class ListController extends Controller 'type' => 'comment_type', 'persistent' => 'comment_is_persistent', 'expiration' => 'comment_expiration', + 'name' => 'comment_name', 'host_name', 'service_description', 'host_display_name', diff --git a/modules/monitoring/application/controllers/ShowController.php b/modules/monitoring/application/controllers/ShowController.php index bfd5167ff..2c7bc6039 100644 --- a/modules/monitoring/application/controllers/ShowController.php +++ b/modules/monitoring/application/controllers/ShowController.php @@ -29,7 +29,6 @@ class ShowController extends Controller 'contact_alias', 'contact_email', 'contact_pager', - 'contact_object_id', 'contact_notify_service_timeperiod', 'contact_notify_service_recovery', 'contact_notify_service_warning', @@ -61,13 +60,13 @@ class ShowController extends Controller 'service_description', 'notification_output', 'notification_contact_name', - 'notification_start_time', + 'notification_timestamp', 'notification_state', 'host_display_name', 'service_display_name' )); - $notifications->where('contact_object_id', $contact->contact_object_id); + $notifications->where('notification_contact_name', $contactName); $this->applyRestriction('monitoring/filter/objects', $notifications); $this->view->notifications = $notifications; $this->setupLimitControl(); diff --git a/modules/monitoring/application/controllers/TimelineController.php b/modules/monitoring/application/controllers/TimelineController.php index d97224564..c0aa3ff61 100644 --- a/modules/monitoring/application/controllers/TimelineController.php +++ b/modules/monitoring/application/controllers/TimelineController.php @@ -46,34 +46,33 @@ class TimelineController extends Controller ), array( 'notify' => array( + 'class' => 'timeline-notification', 'detailUrl' => $detailUrl, - 'label' => mt('monitoring', 'Notifications'), - 'color' => '#3a71ea' + 'label' => mt('monitoring', 'Notifications') ), 'hard_state' => array( + 'class' => 'timeline-hard-state', 'detailUrl' => $detailUrl, - 'label' => mt('monitoring', 'Hard state changes'), - 'color' => '#ff7000' + 'label' => mt('monitoring', 'Hard state changes') ), 'comment' => array( + 'class' => 'timeline-comment', 'detailUrl' => $detailUrl, - 'label' => mt('monitoring', 'Comments'), - 'color' => '#79bdba' + 'label' => mt('monitoring', 'Comments') ), 'ack' => array( - 'detailUrl' => $detailUrl, - 'label' => mt('monitoring', 'Acknowledgements'), - 'color' => '#a2721d' + 'class' => 'timeline-ack', + 'label' => mt('monitoring', 'Acknowledgements') ), 'dt_start' => array( + 'class' => 'timeline-downtime-start', 'detailUrl' => $detailUrl, - 'label' => mt('monitoring', 'Started downtimes'), - 'color' => '#8e8e8e' + 'label' => mt('monitoring', 'Started downtimes') ), 'dt_end' => array( + 'class' => 'timeline-downtime-end', 'detailUrl' => $detailUrl, - 'label' => mt('monitoring', 'Ended downtimes'), - 'color' => '#d5d6ad' + 'label' => mt('monitoring', 'Ended downtimes') ) ) ); diff --git a/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php b/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php index cd5b866b7..180719ca7 100644 --- a/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Monitoring\Forms\Command\Object; use DateTime; use DateInterval; +use Icinga\Application\Config; use Icinga\Module\Monitoring\Command\Object\AcknowledgeProblemCommand; use Icinga\Web\Notification; @@ -40,6 +41,8 @@ class AcknowledgeProblemCommandForm extends ObjectsCommandForm */ public function createElements(array $formData = array()) { + $config = Config::module('monitoring'); + $this->addElements(array( array( 'textarea', @@ -59,6 +62,7 @@ class AcknowledgeProblemCommandForm extends ObjectsCommandForm 'persistent', array( 'label' => $this->translate('Persistent Comment'), + 'value' => (bool) $config->get('settings', 'acknowledge_persistent', false), 'description' => $this->translate( 'If you would like the comment to remain even when the acknowledgement is removed, check this' . ' option.' @@ -70,6 +74,7 @@ class AcknowledgeProblemCommandForm extends ObjectsCommandForm 'expire', array( 'label' => $this->translate('Use Expire Time'), + 'value' => (bool) $config->get('settings', 'acknowledge_expire', false), 'description' => $this->translate( 'If the acknowledgement should expire, check this option.' ), @@ -109,7 +114,7 @@ class AcknowledgeProblemCommandForm extends ObjectsCommandForm 'sticky', array( 'label' => $this->translate('Sticky Acknowledgement'), - 'value' => true, + 'value' => (bool) $config->get('settings', 'acknowledge_sticky', false), 'description' => $this->translate( 'If you want the acknowledgement to remain until the host or service recovers even if the host' . ' or service changes state, check this option.' @@ -121,7 +126,7 @@ class AcknowledgeProblemCommandForm extends ObjectsCommandForm 'notify', array( 'label' => $this->translate('Send Notification'), - 'value' => true, + 'value' => (bool) $config->get('settings', 'acknowledge_notify', true), 'description' => $this->translate( 'If you do not want an acknowledgement notification to be sent out to the appropriate contacts,' . ' uncheck this option.' diff --git a/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php b/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php index 4d6124e0c..3cff2cf7c 100644 --- a/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Monitoring\Forms\Command\Object; +use Icinga\Application\Config; use Icinga\Module\Monitoring\Command\Object\AddCommentCommand; use Icinga\Web\Notification; @@ -53,7 +54,7 @@ class AddCommentCommandForm extends ObjectsCommandForm 'persistent', array( 'label' => $this->translate('Persistent'), - 'value' => true, + 'value' => (bool) Config::module('monitoring')->get('settings', 'comment_persistent', true), 'description' => $this->translate( 'If you uncheck this option, the comment will automatically be deleted the next time Icinga is' . ' restarted.' diff --git a/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php b/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php index b7855294b..db0a619ac 100644 --- a/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php @@ -39,7 +39,7 @@ class CheckNowCommandForm extends ObjectsCommandForm ), 'escape' => false, 'ignore' => true, - 'label' => $this->getView()->icon('reschedule') . $this->translate('Check now'), + 'label' => $this->getView()->icon('arrows-cw') . $this->translate('Check now'), 'type' => 'submit', 'title' => $this->translate('Schedule the next active check to run immediately'), 'value' => $this->translate('Check now') diff --git a/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php b/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php index e526d60e7..3b1cd5927 100644 --- a/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php @@ -20,42 +20,6 @@ class DeleteCommentCommandForm extends CommandForm $this->setAttrib('class', 'inline'); } - /** - * {@inheritdoc} - */ - public function createElements(array $formData = array()) - { - $this->addElements( - array( - array( - 'hidden', - 'comment_id', - array( - 'required' => true, - 'validators' => array('NotEmpty'), - 'decorators' => array('ViewHelper') - ) - ), - array( - 'hidden', - 'comment_is_service', - array( - 'filters' => array('Boolean'), - 'decorators' => array('ViewHelper') - ) - ), - array( - 'hidden', - 'redirect', - array( - 'decorators' => array('ViewHelper') - ) - ) - ) - ); - return $this; - } - /** * {@inheritdoc} */ @@ -80,14 +44,59 @@ class DeleteCommentCommandForm extends CommandForm return $this; } + /** + * {@inheritdoc} + */ + public function createElements(array $formData = array()) + { + $this->addElements( + array( + array( + 'hidden', + 'comment_id', + array( + 'required' => true, + 'validators' => array('NotEmpty'), + 'decorators' => array('ViewHelper') + ) + ), + array( + 'hidden', + 'comment_is_service', + array( + 'filters' => array('Boolean'), + 'decorators' => array('ViewHelper') + ) + ), + array( + 'hidden', + 'comment_name', + array( + 'decorators' => array('ViewHelper') + ) + ), + array( + 'hidden', + 'redirect', + array( + 'decorators' => array('ViewHelper') + ) + ) + ) + ); + return $this; + } + /** * {@inheritdoc} */ public function onSuccess() { $cmd = new DeleteCommentCommand(); - $cmd->setIsService($this->getElement('comment_is_service')->getValue()) - ->setCommentId($this->getElement('comment_id')->getValue()); + $cmd + ->setCommentId($this->getElement('comment_id')->getValue()) + ->setCommentName($this->getElement('comment_name')->getValue()) + ->setIsService($this->getElement('comment_is_service')->getValue()); $this->getTransport($this->request)->send($cmd); $redirect = $this->getElement('redirect')->getValue(); if (! empty($redirect)) { diff --git a/modules/monitoring/application/forms/Command/Object/DeleteCommentsCommandForm.php b/modules/monitoring/application/forms/Command/Object/DeleteCommentsCommandForm.php index 2b5b9a79e..71886639e 100644 --- a/modules/monitoring/application/forms/Command/Object/DeleteCommentsCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/DeleteCommentsCommandForm.php @@ -72,6 +72,7 @@ class DeleteCommentsCommandForm extends CommandForm $cmd = new DeleteCommentCommand(); $cmd ->setCommentId($comment->id) + ->setCommentName($comment->name) ->setIsService(isset($comment->service_description)); $this->getTransport($this->request)->send($cmd); } diff --git a/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php b/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php index 15c0e8ebd..be2109eec 100644 --- a/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php @@ -68,6 +68,13 @@ class DeleteDowntimeCommandForm extends CommandForm 'filters' => array('Boolean') ) ), + array( + 'hidden', + 'downtime_name', + array( + 'decorators' => array('ViewHelper') + ) + ), array( 'hidden', 'redirect', @@ -86,8 +93,10 @@ class DeleteDowntimeCommandForm extends CommandForm public function onSuccess() { $cmd = new DeleteDowntimeCommand(); - $cmd->setDowntimeId($this->getElement('downtime_id')->getValue()); - $cmd->setIsService($this->getElement('downtime_is_service')->getValue()); + $cmd + ->setDowntimeId($this->getElement('downtime_id')->getValue()) + ->setDowntimeName($this->getElement('downtime_name')->getValue()) + ->setIsService($this->getElement('downtime_is_service')->getValue()); $this->getTransport($this->request)->send($cmd); $redirect = $this->getElement('redirect')->getValue(); diff --git a/modules/monitoring/application/forms/Command/Object/DeleteDowntimesCommandForm.php b/modules/monitoring/application/forms/Command/Object/DeleteDowntimesCommandForm.php index 52590d145..1e0399c88 100644 --- a/modules/monitoring/application/forms/Command/Object/DeleteDowntimesCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/DeleteDowntimesCommandForm.php @@ -72,6 +72,7 @@ class DeleteDowntimesCommandForm extends CommandForm $delDowntime = new DeleteDowntimeCommand(); $delDowntime ->setDowntimeId($downtime->id) + ->setDowntimeName($downtime->name) ->setIsService(isset($downtime->service_description)); $this->getTransport($this->request)->send($delDowntime); } diff --git a/modules/monitoring/application/forms/Command/Object/RemoveAcknowledgementCommandForm.php b/modules/monitoring/application/forms/Command/Object/RemoveAcknowledgementCommandForm.php index 416e02e59..7d6672225 100644 --- a/modules/monitoring/application/forms/Command/Object/RemoveAcknowledgementCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/RemoveAcknowledgementCommandForm.php @@ -70,8 +70,8 @@ class RemoveAcknowledgementCommandForm extends ObjectsCommandForm 'ignore' => true, 'label' => $this->getSubmitLabel(), 'title' => $this->translatePlural( - 'Remove problem acknowledgement', - 'Remove problem acknowledgements', + 'Remove acknowledgement', + 'Remove acknowledgements', count($this->objects) ), 'type' => 'submit' @@ -89,8 +89,8 @@ class RemoveAcknowledgementCommandForm extends ObjectsCommandForm $label = $this->getView()->icon('cancel'); if ($this->isLabelEnabled()) { $label .= $this->translatePlural( - 'Remove problem acknowledgement', - 'Remove problem acknowledgements', + 'Remove acknowledgement', + 'Remove acknowledgements', count($this->objects) ); } @@ -111,8 +111,8 @@ class RemoveAcknowledgementCommandForm extends ObjectsCommandForm } Notification::success(mtp( 'monitoring', - 'Removing problem acknowledgement..', - 'Removing problem acknowledgements..', + 'Removing acknowledgement..', + 'Removing acknowledgements..', count($this->objects) )); diff --git a/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php b/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php index b11d2d2d2..6bbd1a7f4 100644 --- a/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Monitoring\Forms\Command\Object; +use Icinga\Application\Config; use Icinga\Module\Monitoring\Command\Object\ScheduleHostCheckCommand; use Icinga\Web\Notification; @@ -17,6 +18,8 @@ class ScheduleHostCheckCommandForm extends ScheduleServiceCheckCommandForm */ public function createElements(array $formData = array()) { + $config = Config::module('monitoring'); + parent::createElements($formData); $this->addElements(array( array( @@ -24,6 +27,7 @@ class ScheduleHostCheckCommandForm extends ScheduleServiceCheckCommandForm 'all_services', array( 'label' => $this->translate('All Services'), + 'value' => (bool) $config->get('settings', 'hostcheck_all_services', false), 'description' => $this->translate( 'Schedule check for all services on the hosts and the hosts themselves.' ) diff --git a/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php b/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php index 003f3f0aa..53c951644 100644 --- a/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php @@ -29,29 +29,26 @@ class ScheduleHostDowntimeCommandForm extends ScheduleServiceDowntimeCommandForm 'description' => $this->translate( 'Schedule downtime for all services on the hosts and the hosts themselves.' ), - 'label' => $this->translate('All Services'), - 'value' => false + 'label' => $this->translate('All Services') ) ); - if (! $this->getBackend()->isIcinga2()) { - $this->addElement( - 'select', - 'child_hosts', - array( - 'description' => $this->translate( - 'Define what should be done with the child hosts of the hosts.' - ), - 'label' => $this->translate('Child Hosts'), - 'multiOptions' => array( - 0 => $this->translate('Do nothing with child hosts'), - 1 => $this->translate('Schedule triggered downtime for all child hosts'), - 2 => $this->translate('Schedule non-triggered downtime for all child hosts') - ), - 'value' => 0 - ) - ); - } + $this->addElement( + 'select', + 'child_hosts', + array( + 'description' => $this->translate( + 'Define what should be done with the child hosts of the hosts.' + ), + 'label' => $this->translate('Child Hosts'), + 'multiOptions' => array( + 0 => $this->translate('Do nothing with child hosts'), + 1 => $this->translate('Schedule triggered downtime for all child hosts'), + 2 => $this->translate('Schedule non-triggered downtime for all child hosts') + ), + 'value' => 0 + ) + ); return $this; } diff --git a/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php b/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php index 6bc691f26..ba7a049f6 100644 --- a/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Monitoring\Forms\Command\Object; +use Icinga\Application\Config; use Icinga\Module\Monitoring\Command\Object\SendCustomNotificationCommand; use Icinga\Web\Notification; @@ -34,6 +35,8 @@ class SendCustomNotificationCommandForm extends ObjectsCommandForm */ public function createElements(array $formData = array()) { + $config = Config::module('monitoring'); + $this->addElements(array( array( 'textarea', @@ -53,25 +56,29 @@ class SendCustomNotificationCommandForm extends ObjectsCommandForm 'forced', array( 'label' => $this->translate('Forced'), - 'value' => false, + 'value' => (bool) $config->get('settings', 'custom_notification_forced', false), 'description' => $this->translate( 'If you check this option, the notification is sent out regardless of time restrictions and' . ' whether or not notifications are enabled.' ) ) - ), - array( + ) + )); + + if (! $this->getBackend()->isIcinga2()) { + $this->addElement( 'checkbox', 'broadcast', array( 'label' => $this->translate('Broadcast'), - 'value' => false, + 'value' => (bool) $config->get('settings', 'custom_notification_broadcast', false), 'description' => $this->translate( 'If you check this option, the notification is sent out to all normal and escalated contacts.' ) ) - ) - )); + ); + } + return $this; } @@ -87,8 +94,10 @@ class SendCustomNotificationCommandForm extends ObjectsCommandForm ->setObject($object) ->setComment($this->getElement('comment')->getValue()) ->setAuthor($this->request->getUser()->getUsername()) - ->setForced($this->getElement('forced')->isChecked()) - ->setBroadcast($this->getElement('broadcast')->isChecked()); + ->setForced($this->getElement('forced')->isChecked()); + if (($broadcast = $this->getElement('broadcast')) !== null) { + $notification->setBroadcast($broadcast->isChecked()); + } $this->getTransport($this->request)->send($notification); } Notification::success($this->translatePlural( diff --git a/modules/monitoring/application/forms/Config/BackendConfigForm.php b/modules/monitoring/application/forms/Config/BackendConfigForm.php index 78cb049de..e7fdba67c 100644 --- a/modules/monitoring/application/forms/Config/BackendConfigForm.php +++ b/modules/monitoring/application/forms/Config/BackendConfigForm.php @@ -147,12 +147,6 @@ class BackendConfigForm extends ConfigForm } $backendConfig->merge($data); - foreach ($backendConfig->toArray() as $k => $v) { - if ($v === null) { - unset($backendConfig->$k); - } - } - $this->config->setSection($name, $backendConfig); return $this; } diff --git a/modules/monitoring/application/forms/Config/Transport/ApiTransportForm.php b/modules/monitoring/application/forms/Config/Transport/ApiTransportForm.php new file mode 100644 index 000000000..3a54ef2e9 --- /dev/null +++ b/modules/monitoring/application/forms/Config/Transport/ApiTransportForm.php @@ -0,0 +1,73 @@ +setName('form_config_command_transport_api'); + } + + /** + * {@inheritdoc} + */ + public function createElements(array $formData = array()) + { + $this->addElements(array( + array( + 'text', + 'host', + array( + 'required' => true, + 'label' => $this->translate('Host'), + 'description' => $this->translate( + 'Hostname or address of the remote Icinga instance' + ) + ) + ), + array( + 'number', + 'port', + array( + 'required' => true, + 'label' => $this->translate('Port'), + 'description' => $this->translate('SSH port to connect to on the remote Icinga instance'), + 'value' => 5665 + ) + ), + array( + 'text', + 'username', + array( + 'required' => true, + 'label' => $this->translate('API Username'), + 'description' => $this->translate( + 'User to log in as on the remote Icinga instance. Please note that key-based SSH login must be' + . ' possible for this user' + ) + ) + ), + array( + 'password', + 'password', + array( + 'required' => true, + 'label' => $this->translate('API Password'), + 'description' => $this->translate( + 'User to log in as on the remote Icinga instance. Please note that key-based SSH login must be' + . ' possible for this user' + ) + ) + ) + )); + + return $this; + } +} diff --git a/modules/monitoring/application/forms/Config/TransportConfigForm.php b/modules/monitoring/application/forms/Config/TransportConfigForm.php index 4f14bfd5e..79ac1741b 100644 --- a/modules/monitoring/application/forms/Config/TransportConfigForm.php +++ b/modules/monitoring/application/forms/Config/TransportConfigForm.php @@ -7,8 +7,10 @@ use InvalidArgumentException; use Icinga\Exception\IcingaException; use Icinga\Exception\NotFoundError; use Icinga\Forms\ConfigForm; +use Icinga\Module\Monitoring\Command\Transport\ApiCommandTransport; use Icinga\Module\Monitoring\Command\Transport\LocalCommandFile; use Icinga\Module\Monitoring\Command\Transport\RemoteCommandFile; +use Icinga\Module\Monitoring\Forms\Config\Transport\ApiTransportForm; use Icinga\Module\Monitoring\Forms\Config\Transport\LocalTransportForm; use Icinga\Module\Monitoring\Forms\Config\Transport\RemoteTransportForm; @@ -68,7 +70,7 @@ class TransportConfigForm extends ConfigForm * * @param string $type The transport type for which to return a form * - * @return Form + * @return \Icinga\Web\Form * * @throws InvalidArgumentException In case the given transport type is invalid */ @@ -79,6 +81,8 @@ class TransportConfigForm extends ConfigForm return new LocalTransportForm(); case RemoteCommandFile::TRANSPORT; return new RemoteTransportForm(); + case ApiCommandTransport::TRANSPORT: + return new ApiTransportForm(); default: throw new InvalidArgumentException( sprintf($this->translate('Invalid command transport type "%s" given'), $type) @@ -163,12 +167,6 @@ class TransportConfigForm extends ConfigForm } $transportConfig->merge($data); - foreach ($transportConfig->toArray() as $k => $v) { - if ($v === null) { - unset($transportConfig->$k); - } - } - $this->config->setSection($name, $transportConfig); return $this; } @@ -223,7 +221,8 @@ class TransportConfigForm extends ConfigForm $transportTypes = array( LocalCommandFile::TRANSPORT => $this->translate('Local Command File'), - RemoteCommandFile::TRANSPORT => $this->translate('Remote Command File') + RemoteCommandFile::TRANSPORT => $this->translate('Remote Command File'), + ApiCommandTransport::TRANSPORT => $this->translate('Icinga 2 API') ); $transportType = isset($formData['transport']) ? $formData['transport'] : null; diff --git a/modules/monitoring/application/forms/StatehistoryForm.php b/modules/monitoring/application/forms/StatehistoryForm.php index 200830567..3a7c10df5 100644 --- a/modules/monitoring/application/forms/StatehistoryForm.php +++ b/modules/monitoring/application/forms/StatehistoryForm.php @@ -71,8 +71,7 @@ class StatehistoryForm extends Form strtotime('midnight 8 months ago') => $this->translate('8 Months'), strtotime('midnight 12 months ago') => $this->translate('1 Year'), strtotime('midnight 24 months ago') => $this->translate('2 Years') - ), - 'class' => 'autosubmit' + ) ) ); $this->addElement( @@ -83,8 +82,7 @@ class StatehistoryForm extends Form 'value' => $this->getRequest()->getParam('to', time()), 'multiOptions' => array( time() => $this->translate('Today') - ), - 'class' => 'autosubmit' + ) ) ); @@ -98,8 +96,7 @@ class StatehistoryForm extends Form 'multiOptions' => array( 'services' => $this->translate('Services'), 'hosts' => $this->translate('Hosts') - ), - 'class' => 'autosubmit' + ) ) ); if ($objectType === 'services') { @@ -118,8 +115,7 @@ class StatehistoryForm extends Form 'cnt_warning_hard' => $this->translate('Warning'), 'cnt_unknown_hard' => $this->translate('Unknown'), 'cnt_ok' => $this->translate('Ok') - ), - 'class' => 'autosubmit' + ) ) ); } else { @@ -137,8 +133,7 @@ class StatehistoryForm extends Form 'cnt_up' => $this->translate('Up'), 'cnt_down_hard' => $this->translate('Down'), 'cnt_unreachable_hard' => $this->translate('Unreachable') - ), - 'class' => 'autosubmit' + ) ) ); } diff --git a/modules/monitoring/application/locale/ar_SA/LC_MESSAGES/monitoring.mo b/modules/monitoring/application/locale/ar_SA/LC_MESSAGES/monitoring.mo new file mode 100644 index 000000000..ceb13d3a4 Binary files /dev/null and b/modules/monitoring/application/locale/ar_SA/LC_MESSAGES/monitoring.mo differ diff --git a/modules/monitoring/application/locale/ar_SA/LC_MESSAGES/monitoring.po b/modules/monitoring/application/locale/ar_SA/LC_MESSAGES/monitoring.po new file mode 100644 index 000000000..15e202b76 --- /dev/null +++ b/modules/monitoring/application/locale/ar_SA/LC_MESSAGES/monitoring.po @@ -0,0 +1,4346 @@ +# Icinga Web 2 - Head for multiple monitoring backends. +# Copyright (C) 2016 Icinga Development Team +# This file is distributed under the same license as Monitoring Module. +# +# Munzir Taha , 2016. +msgid "" +msgstr "" +"Project-Id-Version: Monitoring Module (2.3.2)\n" +"Report-Msgid-Bugs-To: dev@icinga.org\n" +"POT-Creation-Date: 2016-05-25 05:05+0300\n" +"PO-Revision-Date: 2016-06-02 10:09+0300\n" +"Last-Translator: Munzir Taha \n" +"Language: ar_SA\n" +"Language-Team: English \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Lokalize 2.0\n" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:375 +msgid " Down Hosts (Handled)" +msgstr "أجهزة مضيفة متوقفة (عولجت)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:376 +msgid " Down Hosts (Unhandled)" +msgstr "أجهزة مضيفة متوقفة ( لم تعالج)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:343 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:397 +msgid " Down Services (Handled)" +msgstr "خدمات متوقفة (عُولجت)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:344 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:398 +msgid " Down Services (Unhandled)" +msgstr "خدمات متوقفة (لم تُعالج)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:379 +msgid " Pending Hosts" +msgstr "أجهزة مضيفة معلقة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:347 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:401 +msgid " Pending Services" +msgstr "خدمات معلقة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:377 +msgid " Unreachable Hosts (Handled)" +msgstr "أجهزة مضيفة لا يمكن الوصول إليها (عُولجت)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:378 +msgid " Unreachable Hosts (Unhandled)" +msgstr "أجهزة مضيفة لا يمكن الوصول إليها (لم تُعالج)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:345 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:399 +msgid " Unreachable Services (Handled)" +msgstr "حدمات لا يمكن الوصول إليها (عولجت)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:346 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:400 +msgid " Unreachable Services (Unhandled)" +msgstr "حدمات لا يمكن الوصول إليها (لم تُعالج)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:374 +msgid " Up Hosts" +msgstr "أجهزة مضيفة قيد التشغيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:394 +msgid " Up Services" +msgstr "خدمات قيد التشغيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:341 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:395 +msgid " Warning Services (Handled)" +msgstr "خدمات تحذيرية (عولجت)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:342 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:396 +msgid " Warning Services (Unhandled)" +msgstr "خدمات تحذيرية (لم تعالج)" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:65 +#, php-format +msgctxt "Last format parameter represents the time running" +msgid "%1$s has been up and running with PID %2$d %3$s" +msgstr "%1$s ما زالت قيد التشغيل ب PID %2$d %3$s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:35 +msgctxt "date.verbose" +msgid "%A, %B %e, %Y" +msgstr "%A, %B %e, %Y" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:75 +#, php-format +msgid "%d Active" +msgid_plural "%d Active" +msgstr[0] "%d نشطة" +msgstr[1] "%d نشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:55 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:111 +#, php-format +msgid "%d Disabled" +msgid_plural "%d Disabled" +msgstr[0] "%d متعطلة" +msgstr[1] "%d متعطلة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:37 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:93 +#, php-format +msgid "%d Passive" +msgid_plural "%d Passive" +msgstr[0] "%d سلبية" +msgstr[1] "%d سلبية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/eventgrid.phtml:38 +#, php-format +msgid "%d hosts down on %s" +msgstr "%d أجهزة مضيفة متوقفة على %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/eventgrid.phtml:16 +#, php-format +msgid "%d hosts ok on %s" +msgstr "%d أجهزة مضيفة سليمة على %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/eventgrid.phtml:21 +#, php-format +msgid "%d hosts unreachable on %s" +msgstr "%d أجهزة مضيفة لا يمكن الوصول إليها على %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/Perfdata.php:97 +#, php-format +msgid "%d more ..." +msgstr "%d المزيد ..." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notifications.phtml:54 +#, php-format +msgid "%d notifications have been sent for this issue." +msgstr "%d من الإخطارات أرسلت بخصوص هذه المشكلة." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:118 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:117 +#, php-format +msgid "%d scheduled downtime" +msgid_plural "%d scheduled downtimes" +msgstr[0] "%d زمن توقف مجدول" +msgstr[1] "%d من أزمنة التوقف المجدولة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/eventgrid.phtml:26 +#, php-format +msgid "%d services critical on %s" +msgstr "%d من الخدمات الضرورية على %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/eventgrid.phtml:48 +#, php-format +msgid "%d services ok on %s" +msgstr "%d من الخدمات سليمة على %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/eventgrid.phtml:43 +#, php-format +msgid "%d services unknown on %s" +msgstr "%d من الخدمات مجهولة على %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/eventgrid.phtml:32 +#, php-format +msgid "%d services warning on %s" +msgstr "%d من الخدمات التحذيرية على %s" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:136 +#, php-format +msgid "%d unhandled hosts down" +msgstr "%d أجهزة مضيفة لم تُعالج" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:27 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:27 +#, php-format +msgid "%d unhandled problems" +msgstr "%d مشاكل لم تُعالج" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:148 +#, php-format +msgid "%d unhandled services critical" +msgstr "%d من الخدمات غير المعالجة ضرورية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:46 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:44 +#, php-format +msgid "%s acknowledgement" +msgid_plural "%s acknowledgements" +msgstr[0] "%s إشعار" +msgstr[1] "%s من الإشعارات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:80 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:79 +#, php-format +msgid "%s comment" +msgid_plural "%s comments" +msgstr[0] "%s تعليق" +msgstr[1] "%s من التعليقات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/not-running.phtml:7 +#, php-format +msgid "%s is currently not up and running" +msgstr "%s حاليا لا تعمل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/components/selectioninfo.phtml:12 +#, php-format +msgctxt "Multi-selection count" +msgid "%s row(s) selected" +msgstr "%s من الصفوف تم اختيارها" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:19 +#, php-format +msgid "%u Active" +msgid_plural "%u Active" +msgstr[0] "%u نشطة" +msgstr[1] "%u نشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/components/hostssummary.phtml:12 +#, php-format +msgid "%u Host" +msgid_plural "%u Hosts" +msgstr[0] "%u مضيف" +msgstr[1] "%u أجهزة مضيفة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/problem_hosts.phtml:11 +#, php-format +msgid "%u Host DOWN" +msgid_plural "%u Hosts DOWN" +msgstr[0] "%u مضيف متوقف" +msgstr[1] "%u أجهزة مضيفة متوقفة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:20 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:141 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:220 +#, php-format +msgid "%u Host Disabled" +msgid_plural "%u Hosts Disabled" +msgstr[0] "%u مضيف معطل" +msgstr[1] "%u من الأجهزة المضيفة معطلة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:50 +#, php-format +msgid "%u Host Flapping" +msgid_plural "%u Hosts Flapping" +msgstr[0] "%u مضيف مذبذب" +msgstr[1] "%u أجهزة مضيفة مذبذبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/ok_hosts.phtml:34 +#, php-format +msgid "%u Host PENDING" +msgid_plural "%u Hosts PENDING" +msgstr[0] "%u مضيف معلق" +msgstr[1] "%u أجهزة مضيفة معلقة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/problem_hosts.phtml:30 +#, php-format +msgid "%u Host UNREACHABLE" +msgid_plural "%u Hosts UNREACHABLE" +msgstr[0] "%u مضيف لا يمكن الوصول إليه" +msgstr[1] "%u من الأجهزة المضيفة لا يمكن الوصول إليها" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/ok_hosts.phtml:16 +#, php-format +msgid "%u Host UP" +msgid_plural "%u Hosts UP" +msgstr[0] "%u مضيف سليم" +msgstr[1] "%u أجهزة مضيفة سليمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/components/servicesummary.phtml:13 +#, php-format +msgid "%u Service" +msgid_plural "%u Services" +msgstr[0] "%u خدمة" +msgstr[1] "%u من الخدمات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:75 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:175 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:254 +#, php-format +msgid "%u Service Disabled" +msgid_plural "%u Services Disabled" +msgstr[0] "%u خدمة متعطلة" +msgstr[1] "%u من الخدمات متعطلة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:105 +#, php-format +msgid "%u Service Flapping" +msgid_plural "%u Services Flapping" +msgstr[0] "%u خدمة مذبذبة" +msgstr[1] "%u من الخدمات مذبذبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:80 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:179 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:278 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:325 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:372 +#, php-format +msgid "%u is not checked at all" +msgid_plural "%u are not checked at all" +msgstr[0] "%u لم يتم فحصها مطلقا" +msgstr[1] "%u لم يتم فحصها مطلقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:53 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:152 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:251 +#, php-format +msgid "%u is passively checked" +msgid_plural "%u are passively checked" +msgstr[0] "%u تم فحصها سلبيا" +msgstr[1] "%u تم فحصها سلبيا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hosts.phtml:62 +#, php-format +msgid "%u unhandled service" +msgid_plural "%u unhandled services" +msgstr[0] "%u خدمة لم تُعالج" +msgstr[1] "%u من الخدمات لم تُعالج" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/DataView/DataView.php:235 +msgid "(Case insensitive)" +msgstr "(غير حساسة لحالة الأحرف)" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:72 +msgid "1 Year" +msgstr "سنة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:73 +msgid "2 Years" +msgstr "سنتان" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:69 +msgid "3 Months" +msgstr "3 أشهر" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:103 +msgid "4 Hours" +msgstr "4 ساعات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:70 +msgid "4 Months" +msgstr "4 أشهر" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:71 +msgid "8 Months" +msgstr "8 أشهر" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:448 +msgid "{title}: {value}m max. reaction time" +msgstr "{title}: {value}m أقصى زمن رد فعل" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:438 +msgid "{title}: {value}m min. reaction time" +msgstr "{title}: {value}m أدنى زمن رد فعل" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:289 +msgid "{title}:
{value} of {sum} hosts are {label}" +msgstr "{title}:
{value} من {sum} أجهزة مضيفة {label}" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:240 +msgid "{title}:
{value} of {sum} services are {label}" +msgstr "{title}:
{value} من {sum} خدمات {label}" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:129 +#, php-format +msgid "A command transport with the name \"%s\" does already exist" +msgstr "ناقل أمر باسم \"%s\" موجود مسبقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:46 +msgid "" +"A comment, as entered by the author, associated with the scheduled downtime" +msgstr "تعليق مدخل من قبل المؤلف له ارتباط بزمن التوقف المجدول" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:77 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:212 +msgid "A downtime must not be in the past" +msgstr "زمن التوقف لا يجب أن يكون في الماضي" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:113 +#, php-format +msgid "A monitoring backend with the name \"%s\" does already exist" +msgstr " خلفية المراقبة المسماة \"%s\" موجودة مسبقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notifications.phtml:49 +#, php-format +msgid "A notification has been sent for this issue %s." +msgstr "أُرسل إشعار بخصوص هذه المشكلة %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:67 +msgid "ACKNOWLEDGED" +msgstr "معروف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:72 +msgid "ACKNOWLEDGEMENT REMOVED" +msgstr "تمت إزالة إشعار المعرفة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:30 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:29 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml:77 +msgid "Acknowledge" +msgstr "يعرف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostController.php:118 +msgid "Acknowledge Host Problem" +msgstr "تعرف على مشكلة مضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostsController.php:175 +msgid "Acknowledge Host Problems" +msgstr "تعرف على مشاكل الأجهزة المضيفة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServiceController.php:76 +msgid "Acknowledge Service Problem" +msgstr "تعرف على مشكلة خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServicesController.php:178 +msgid "Acknowledge Service Problems" +msgstr "تعرف على مشاكل خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:34 +msgid "Acknowledge problem" +msgid_plural "Acknowledge problems" +msgstr[0] "تعرف مشكلة" +msgstr[1] "تعرف مشاكل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml:85 +msgid "" +"Acknowledge this problem, suppress all future notifications for it and tag " +"it as being handled" +msgstr "" +"تعرف على هذه المشكلة، واخمد كل الإشعارات المستقبلية لها وضع علامة أنه تم" +" معالجتها" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/service/statusicons.phtml:9 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/host/statusicons.phtml:9 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml:15 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/HostFlags.php:16 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/ServiceFlags.php:12 +msgid "Acknowledged" +msgstr "معروف" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:335 +msgid "Acknowledged Problem Hosts" +msgstr "مضيفون بمشاكل معروفة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:331 +msgid "Acknowledged Problem Services" +msgstr "خدمات بمشاكل معروفة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml:20 +msgid "Acknowledgement" +msgstr "إشعار استلام" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml:35 +#, php-format +msgid "" +"Acknowledgement remains until the %1$s recovers even if the %1$s changes " +"state" +msgstr "ستبقى الإشعارات حتى تتعافي %1$s حتى ولو غيرت %1$s حالتها" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:65 +msgid "Acknowledgements" +msgstr "إشعارات استلام" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:302 +msgid "Acknowledgements Active For At Least Three Days" +msgstr "الإشعارات النشطة لثلاثة أيام على الأقل" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:157 +msgid "Acknowledging problem.." +msgid_plural "Acknowledging problems.." +msgstr[0] "اعتراف بمشكلة.." +msgstr[1] "اعتراف بمشاكل.." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/actions.phtml:41 +msgid "Actions" +msgstr "أفعال" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/service/statusicons.phtml:24 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/host/statusicons.phtml:24 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/HostFlags.php:29 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/ServiceFlags.php:25 +msgid "Active And Passive Checks Disabled" +msgstr "تم تعطيل الفحوصات النشطة والسالبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:38 +msgid "Active Checks" +msgstr "الفحوصات النشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/service/statusicons.phtml:22 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/host/statusicons.phtml:22 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/HostFlags.php:31 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/ServiceFlags.php:27 +msgid "Active Checks Disabled" +msgstr "تم تعطيل الفحوصات النشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:54 +msgid "Active Endpoint" +msgstr "نقطة نهاية نشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:90 +msgid "Active Host Checks" +msgstr "فحوصات المضيف النشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:99 +msgid "Active Service Checks" +msgstr "فحوصات الخدمة نشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:109 +msgid "Active checks" +msgstr "فحوصات نشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:145 +msgid "Actual end time" +msgstr "زمن النهاية الفعلي " + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:140 +msgid "Actual start time" +msgstr "زمن البدء الفعلي" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostController.php:130 +msgid "Add Host Comment" +msgstr "إضافة تعليق مضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostsController.php:163 +msgid "Add Host Comments" +msgstr "إضافة تعليقات مضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServiceController.php:88 +msgid "Add Service Comment" +msgstr "إضافة تعليق خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServicesController.php:166 +msgid "Add Service Comments" +msgstr "إضافة تعليقات خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/comments.phtml:14 +msgid "Add a new comment to this host" +msgstr "إضافة تعليق جديد لهذا المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/comments.phtml:26 +msgid "Add a new comment to this service" +msgstr "إضافة تعليق جديد لهذه الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/comments.phtml:7 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/comments.phtml:19 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php:28 +msgid "Add comment" +msgid_plural "Add comments" +msgstr[0] "إضافة تعليق" +msgstr[1] "إضافة تعليقات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:62 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:61 +msgid "Add comments" +msgstr "إضافة تعليقات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php:85 +msgid "Adding comment.." +msgid_plural "Adding comments.." +msgstr[0] "إضافة تعليق.." +msgstr[1] "إضافة تعليقات.." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:90 +msgid "Address" +msgstr "عنوان" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:241 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:54 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:58 +msgid "Alert Summary" +msgstr "ملخص التحذيرات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:11 +msgid "Alert summary" +msgstr "ملخص التحذيرات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contactgroups.phtml:24 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:326 +msgid "Alias" +msgstr "اسم مستعار" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:39 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:160 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:239 +msgid "All Hosts Enabled" +msgstr "كل المضيفون نشطاء" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:32 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php:26 +msgid "All Services" +msgstr "كل الخدمات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:94 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:194 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:273 +msgid "All Services Enabled" +msgstr "كل الخدمات مفعلة" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php:178 +msgid "All backends are disabled" +msgstr "كل الخلفيات متعطلة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:16 +msgid "Allow acknowledging host and service problems" +msgstr "السماح بالتعرف على مشاكل الخدمة والمضيف" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:24 +msgid "Allow adding and deleting host and service comments" +msgstr "السماح بإضافة وحذف تعليقات الخدمة والمضيف" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:8 +msgid "Allow all commands" +msgstr "السماح بكل الأوامر" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:28 +msgid "Allow commenting on hosts and services" +msgstr "السماح بالتعليق على الخدمات والمضيفين" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:32 +msgid "Allow deleting host and service comments" +msgstr "السماح بحذف تعليقات على المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:44 +msgid "Allow deleting host and service downtimes" +msgstr "السماح بحذف أزمنة توقف المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:60 +msgid "" +"Allow processing commands for toggling active checks on host and service " +"objects" +msgstr "" +"السماح بمعالجة الأوامر لتبديل الفحوصات النشطة على كائنات المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:72 +msgid "" +"Allow processing commands for toggling event handlers on host and service " +"objects" +msgstr "" +"السماح بمعالجة الأوامر لتبديل الفحوصات النشطة على كائنات المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:52 +msgid "" +"Allow processing commands for toggling features on an instance-wide basis" +msgstr "" +"السماح بمعالجة الأوامر لتبديل الفحوصات النشطة على كائنات المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:56 +msgid "" +"Allow processing commands for toggling features on host and service objects" +msgstr "السماح بمعالجة الأوامر لتبديل المميزات على كائنات المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:76 +msgid "" +"Allow processing commands for toggling flap detection on host and service " +"objects" +msgstr "السماح بمعالجة الأوامر لتبديل فحص التقلب على كائنات المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:68 +msgid "" +"Allow processing commands for toggling notifications on host and service " +"objects" +msgstr "السماح بمعالجة الأوامر لتبديل التنبيهات على كائنات المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:64 +msgid "" +"Allow processing commands for toggling passive checks on host and service " +"objects" +msgstr "" +"السماح بمعالجة الأوامر لتبديل الفحوصات السالبة على كائنات المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:48 +msgid "Allow processing host and service check results" +msgstr "السماح بمعالجة نتائج فحص المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:20 +msgid "Allow removing problem acknowledgements" +msgstr "السماح بإزالة إشعارات المشكلة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:36 +msgid "Allow scheduling and deleting host and service downtimes" +msgstr "السماح بجدولة وحذف أزمنة توقف المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:12 +msgid "Allow scheduling host and service checks" +msgstr "السماح بجدولة فحوصات المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:40 +msgid "Allow scheduling host and service downtimes" +msgstr "السماح بجدولة زمن توقف المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:80 +msgid "Allow sending custom notifications for hosts and services" +msgstr "السماح بإرسال تنبيهات مخصصة للأجهزة المضيفة والخدمات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:20 +msgid "Apply" +msgstr "تطبيق" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:39 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:42 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:249 +msgid "Author" +msgstr "المؤلف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:33 +msgid "Average" +msgstr "المتوسط" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:434 +msgid "Avg (min)" +msgstr "المتوسط (الأدنى)" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:74 +#, php-format +msgid "Backend %s is not running" +msgstr "الخلفية %s لا تعمل" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/BackendPage.php:28 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:193 +msgid "Backend Name" +msgstr "اسم الخلفية" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/BackendPage.php:44 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:221 +msgid "Backend Type" +msgstr "نوع الخلفية" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:94 +msgid "Backends" +msgstr "الخلفيات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:67 +msgid "Broadcast" +msgstr "بث" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:59 +msgid "COMMENT" +msgstr "تعليق" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:63 +msgid "COMMENT DELETED" +msgstr "حذف التعليق" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/Service.php:196 +msgid "CRITICAL" +msgstr "ضروري" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:58 +msgctxt "icinga.state" +msgid "CRITICAL" +msgstr "ضروري" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php:214 +msgid "Can't send external Icinga Command. Remote user is missing" +msgstr "لا يمكن إرسال أمر \"إسنجا\" خارجي. مستخدم بعيد مفقود" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php:219 +msgid "" +"Can't send external Icinga Command. The private key for the remote user is " +"missing" +msgstr "" +"لا يمكن إرسال أمر \"إسنجا\" خارجي. المفتاح الخاص بالمستخدم البعيد مفقود" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Controller.php:97 +#, php-format +msgid "" +"Cannot apply restriction %s using the filter %s. You can only use the " +"following columns: %s" +msgstr "" +"لا يمكن تطبيق تقييد %s باستخدام المرشح %s. يمكنك استخدام الأعمدة التالية: %s" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Command/Transport/CommandTransport.php:76 +#, php-format +msgid "" +"Cannot create command transport \"%s\". Invalid transport defined in \"%s\". " +"Use one of \"%s\" or \"%s\"." +msgstr "" +"لا يمكن إنشاء ناقل أمر \"%s\". نقل غير صالح معرف في \"%s\". استخدم واحدا من \"" +"%s\" أو \"%s\"." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:350 +msgid "" +"Cannot find the IDO schema. Please verify that the given database contains " +"the schema and that the configured user has access to it." +msgstr "" +"تعذر العثور على مخطط IDO. الرجاء التأكد أن قاعدة البيانات المعطاة تحتوي على" +" المخطط وأن المستخدم المعد له صلاحية عليه" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:158 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:160 +msgid "Check Execution" +msgstr "فحص التنفيذ" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:113 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:140 +msgid "Check Performance" +msgstr "فحص الأداء" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checksource.phtml:4 +msgid "Check Source" +msgstr "فحص المصدر" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php:51 +msgid "Check Time" +msgstr "فحص الزمن" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checktimeperiod.phtml:4 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checktimeperiod.phtml:15 +msgid "Check Timeperiod" +msgstr "فحص المدة الزمنية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:66 +msgid "Check attempts" +msgstr "فحص المحاولات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/detail-content.phtml:32 +msgid "Check execution" +msgstr "فحص التنفيذ" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:75 +msgid "Check execution time" +msgstr "فحص زمن التنفيذ" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:82 +msgid "Check latency" +msgstr "فحص التأخير" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php:42 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php:45 +msgid "Check now" +msgstr "الفحص الآن" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:12 +msgid "Check result is late" +msgstr "فحص كون النتيجة متأخرة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/LivestatusResourcePage.php:78 +msgid "" +"Check this to not to validate connectivity with the given Livestatus socket" +msgstr "حدد هذا الخيار لعدم التحقق من صحة الاتصال مع مقبس Livestatus معين" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:329 +msgid "Check this to not to validate the IDO schema of the chosen resource." +msgstr "حدد هذا الخيار لعدم التحقق من صحة المخطط IDO للمورد الذي تم اختياره." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/IdoResourcePage.php:185 +msgid "Check this to not to validate the configuration" +msgstr "حدد هذا الخيار لعدم التحقق من الإعداد" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:114 +msgid "Checks" +msgstr "فحوصات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:45 +msgid "Child Hosts" +msgstr "مضيفون تابعون" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/SecurityConfigForm.php:58 +msgid "" +"Comma separated case insensitive list of protected custom variables. Use * " +"as a placeholder for zero or more wildcard characters. Existance of those " +"custom variables will be shown, but their values will be masked." +msgstr "" +"قائمة مفصولة بفواصل لمتغيرات مخصصة ومحمية غير حساسة لحالة الأحرف. " +"استخدم * كعلامة نائبة عن أي عدد من المحارف. وسيظهر ما إذا كانت تلك " +"المتغيرات المخصصة موجودة لكن قيمها ستحجب." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:163 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:165 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/command.phtml:12 +msgid "Command" +msgstr "أمر" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:177 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/LocalTransportForm.php:30 +msgid "Command File" +msgstr "أمر ملف" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/TransportPage.php:14 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/TransportStep.php:43 +msgctxt "setup.page.title" +msgid "Command Transport" +msgstr "ناقل الأمر" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:62 +msgid "Command Transports" +msgstr "نواقل الأمر" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:240 +#, php-format +msgid "Command transport \"%s\" not found" +msgstr "ناقل الأمر \"%s\" غير موجود" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/CommandForm.php:65 +#, php-format +msgid "Command transport \"%s\" not found." +msgstr "ناقل الأمر \"%s\" غير موجود." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:189 +#, php-format +msgid "Command transport \"%s\" successfully removed" +msgstr "تم إزالة ناقل الأمر \"%s\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:229 +#, php-format +msgid "Command transport \"%s\" successfully updated" +msgstr "تم تحديث ناقل الأمر \"%s\"" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/TransportStep.php:98 +#, php-format +msgid "" +"Command transport configuration could not be written to: %s. An error " +"occured:" +msgstr "إعداد ناقل الأمر لا يمكن كتابته على: %s. حدث خطأ:" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/TransportStep.php:90 +#, php-format +msgid "Command transport configuration has been successfully created: %s" +msgstr "تم إنشاء إعداد ناقل الأمر: %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:268 +msgid "Command transport successfully created" +msgstr "تم إنشاء ناقل الأمر بنجاح" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:163 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:70 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtimes/show.phtml:9 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:51 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comments/show.phtml:9 +msgid "Commands" +msgstr "أوامر" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:47 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php:42 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:43 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:65 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:49 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/CommentController.php:55 +msgid "Comment" +msgstr "تعليق" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:437 +msgid "Comment Timestamp" +msgstr "زمن التعليق" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:440 +msgid "Comment Type" +msgstr "نوع التعليق" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:12 +msgid "Comment detail information" +msgstr "معلومات التعليق التفصيلية" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/CommentController.php:48 +msgid "Comment not found" +msgstr "تعليق غير موجود" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml:16 +msgid "Comment was caused by a downtime" +msgstr "تعليق بسبب زمن التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml:6 +msgid "Comment was caused by a flapping host or service" +msgstr "تعليق بسبب خدمة أو مضيف مذبذب" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml:21 +msgid "Comment was caused by an acknowledgement" +msgstr "تعليق بسبب إشعار" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml:11 +msgid "Comment was created by an user" +msgstr "تعليق منشأ من قبل مستخدم" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:200 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:59 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:58 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/comments.phtml:37 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/EventOverviewForm.php:64 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/CommentsController.php:62 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:60 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:410 +msgid "Comments" +msgstr "تعليقات" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php:196 +#, php-format +msgid "Configuration for backend %s is disabled" +msgstr "إعداد الخلفية %s معطل" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:98 +msgid "" +"Configure how to protect your monitoring environment against prying eyes" +msgstr "إعداد كيفية حماية بيئة المراقبة ضد أعين المتطفلين" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:93 +msgid "Configure how to retrieve monitoring information" +msgstr "إعداد كيفية استعادة معلومات المراقبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/CommentsController.php:95 +#, php-format +msgid "Confirm removal of %d comments." +msgstr "تأكيد إزالة %d من التعليقات." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/DowntimesController.php:99 +#, php-format +msgid "Confirm removal of %d downtimes." +msgstr "إعداد إزالة %d من أزمنة التوقف." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contactgroups.phtml:23 +msgid "Contact Group " +msgstr "مجموعة الاتصال" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:381 +msgid "Contact Groups" +msgstr "مجموعات الاتصال" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:6 +msgid "Contact details" +msgstr "تفاصيل الاتصال" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:397 +msgid "Contactgroup Alias" +msgstr "اسم مجموعة الاتصال المستعار" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:396 +msgid "Contactgroup Name" +msgstr "اسم مجموعة الاتصال" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:196 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/contacts.phtml:35 +msgid "Contactgroups" +msgstr "مجموعات الاتصال" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:192 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/contacts.phtml:17 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:307 +msgid "Contacts" +msgstr "جهات الاتصال" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:292 +msgid "Contains host states of each service group." +msgstr "يحتوي على حالات المضيف لكل مجموعة خدمة." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:233 +msgid "Contains service states for each service group." +msgstr "يحتوي حالات الخدمة لكل مجموعة خدمة." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:47 +msgid "Could not find any valid SSH resources" +msgstr "تعذر العثور على موارد SSH صالحة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:65 +msgid "" +"Could not find any valid monitoring backend resources. Please configure a " +"database resource first." +msgstr "" +"تعذر العثور على موارد مراقبة خلفية صالحة. الرجاء إعداد " +"مورد لقاعدة البيانات أولا." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:254 +msgid "Create New Command Transport" +msgstr "إنشاء ناقل أمر جديد" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:93 +msgid "Create New Monitoring Backend" +msgstr "إنشاء خلفية مراقبة جديدة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:64 +msgid "Create a New Command Transport" +msgstr "إنشاء ناقل أمر جديد" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:9 +msgid "Create a New Monitoring Backend" +msgstr "إنشاء خلفية مراقبة جديدة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:70 +msgid "Create a new command transport" +msgstr "إنشاء ناقل أمر جديد" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:15 +msgid "Create a new monitoring backend" +msgstr "إنشاء خلفية مراقبة جديدة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:52 +msgid "Created" +msgstr "أُنشيء" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/Perfdata.php:41 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:117 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:257 +msgid "Critical" +msgstr "ضروري" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:160 +msgid "Current Downtimes" +msgstr "أزمنة التوقف الحالية" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:174 +msgid "Current Host State" +msgstr "حالة المضيفين الحالية" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:258 +msgid "Current Incidents" +msgstr "الحوادث الحالية" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:169 +msgid "Current Service State" +msgstr "حالة الخدمة الحالية" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:88 +msgid "Current State" +msgstr "الحالة الحالية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/detail-content.phtml:43 +msgid "Custom Variables" +msgstr "متغيرات مخصصة" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/Host.php:185 +msgid "DOWN" +msgstr "متوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:124 +msgctxt "icinga.state" +msgid "DOWN" +msgstr "متوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:83 +msgid "DOWNTIME DELETED" +msgstr "حُذف زمن التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:108 +msgid "DOWNTIME END" +msgstr "نهاية زمن التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:103 +msgid "DOWNTIME START" +msgstr "بداية زمن التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:105 +msgid "Database Name" +msgstr "اسم قاعدة البيانات" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:84 +msgid "Database Resource" +msgstr "مورد قاعدة بيانات" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:93 +msgid "Database Type" +msgstr "نوع قاعدة البيانات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:42 +msgid "Date and time this downtime was entered" +msgstr "تاريخ ووقت إدخال زمن التوقف هذا" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:642 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:645 +msgid "Day" +msgstr "يوم" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:463 +msgid "Defect Chart" +msgstr "رسم بياني للعيوب" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:484 +msgid "Defects" +msgstr "العيوب" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:43 +msgid "Define what should be done with the child hosts of the hosts." +msgstr "عرف ما يتحتم عمله مع المضيفين التابعين." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php:76 +msgid "Delete this comment" +msgstr "احذف هذا التعليق" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php:40 +msgid "Delete this downtime" +msgstr "حذف زمن التوقف هذا" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php:96 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/DeleteCommentsCommandForm.php:83 +msgid "Deleting comment.." +msgid_plural "Deleting comments.." +msgstr[0] "حذف تعليق.." +msgstr[1] "حذف تعليقات.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php:97 +msgid "Deleting downtime." +msgstr "حذف زمن التوقف." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/DeleteDowntimesCommandForm.php:83 +msgid "Deleting downtime.." +msgid_plural "Deleting downtimes.." +msgstr[0] "حذف زمن التوقف.." +msgstr[1] "حذف أزمنة التوقف.." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:10 +msgid "Details" +msgstr "تفاصيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/DisableNotificationsExpireCommandForm.php:24 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HealthController.php:172 +msgid "Disable Notifications" +msgstr "تعطيل التنبيهات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:185 +msgid "Disable This Backend" +msgstr "تعطيل هذه الخلفية" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:69 +msgid "Disable notifications for a specific time on a program-wide basis" +msgstr "تعطيل التنبيهات لوقت محدد على أساس البرنامج بأكمله" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:71 +msgid "Disable temporarily" +msgstr "تعطيل مؤقت" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:327 +msgid "Disabled Host Checks" +msgstr "تعطيل فحوصات المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:319 +msgid "Disabled Host Notifications" +msgstr "تنبيهات المضيف المعطلة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:323 +msgid "Disabled Service Checks" +msgstr "فحوصات الخدمة المعطلة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:315 +msgid "Disabled Service Notifications" +msgstr "تنبيهات الخدمة المعطلة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:130 +msgid "Disabling active checks.." +msgstr "تعطيل الفحوصات النشطة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:222 +msgid "Disabling active host checks.." +msgstr "تعطيل فحوصات المضيف النشطة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:226 +msgid "Disabling active service checks.." +msgstr "تعطيل فحوصات الخدمة النشطة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:146 +msgid "Disabling event handler.." +msgstr "تعطيل معالج الحدث.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:230 +msgid "Disabling event handlers.." +msgstr "تعطيل معالجات الحدث.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:150 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:234 +msgid "Disabling flap detection.." +msgstr "تعطيل فحص التذبذب.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/DisableNotificationsExpireCommandForm.php:61 +msgid "Disabling host and service notifications.." +msgstr "تعطيل تنبيهات المضيف والخدمة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:142 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:238 +msgid "Disabling notifications.." +msgstr "تعطيل التنبيهات.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:242 +msgid "Disabling obsessing over hosts.." +msgstr "تعطيل التوجس على المضيفين.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:246 +msgid "Disabling obsessing over services.." +msgstr "تعطيل التوجس على الخدمات.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:138 +msgid "Disabling obsessing.." +msgstr "تعطيل التوجس.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:134 +msgid "Disabling passive checks.." +msgstr "تعطيل الفحوصات السلبية.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:250 +msgid "Disabling passive host checks.." +msgstr "تعطيل فحوصات المضيف السلبية.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:254 +msgid "Disabling passive service checks.." +msgstr "تعطيل فحوصات الخدمة السلبية.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:258 +msgid "Disabling performance data.." +msgstr "تعطيل بيانات الأداء.." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/CommentController.php:56 +msgid "Display detailed information about a comment." +msgstr "عرض معلومات مفصلة عن تعليق." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/DowntimeController.php:66 +msgid "Display detailed information about a downtime." +msgstr "عرض معلومات مفصلة عن توقف." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/CommentsController.php:64 +msgid "Display detailed information about multiple comments." +msgstr "عرض معلومات تفصيلية عن تعليقات متعددة." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/DowntimesController.php:70 +msgid "Display detailed information about multiple downtimes." +msgstr "عرض معلومات تفصيلية عن توقفات متعددة." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Navigation/ActionForm.php:26 +msgid "" +"Display this action only for objects matching this filter. Leave it blank if " +"you want this action being displayed regardless of the object" +msgstr "" +"عرض هذا الفعل فقط للكائنات الموافقة لهذا المرشح. اتركه فارغا " +"إذا كنت تريد لهذا الفعل أن يعرض بغض النظر عن الكائن" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:47 +msgid "Do nothing with child hosts" +msgstr "لا تفعل شيئا بالمضيفين التابعين" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:138 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:307 +msgid "Down" +msgstr "متوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml:15 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/DowntimeController.php:65 +msgid "Downtime" +msgstr "زمن التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/DowntimeController.php:57 +msgid "Downtime not found" +msgstr "زمن التوقف غير موجود" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:204 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:96 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:95 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:41 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/EventOverviewForm.php:54 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/DowntimesController.php:69 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:212 +msgid "Downtimes" +msgstr "أزمنة التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:306 +msgid "Downtimes Active For More Than Three Days" +msgstr "أزمنة التوقف النشطة لأكثر من ثلاثة أيام" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:53 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:135 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:254 +msgid "Duration" +msgstr "المدة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:3 +msgctxt "Downtime status" +msgid "ENDS" +msgstr "نهايات" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:155 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:168 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/SecurityStep.php:80 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/TransportStep.php:102 +#, php-format +msgid "ERROR: %s" +msgstr "خطأ: %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:6 +msgctxt "Downtime status" +msgid "EXPIRES" +msgstr "انتهاء صلاحية" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:210 +#, php-format +msgid "Edit Command Transport %s" +msgstr "تحرير ناقل الأمر %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:51 +#, php-format +msgid "Edit Monitoring Backend %s" +msgstr "تحرير خلفية المراقبة %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:90 +#, php-format +msgid "Edit command transport %s" +msgstr "تحرير ناقل الأمر %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:35 +#, php-format +msgid "Edit monitoring backend %s" +msgstr "تحرير خلفية المراقبة %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:24 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contacts.phtml:21 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contacts.phtml:42 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:327 +msgid "Email" +msgstr "بريد إلكتروني" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:129 +msgid "Enabling active checks.." +msgstr "تفعيل فحوصات نشطة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:221 +msgid "Enabling active host checks.." +msgstr "تفعيل فحوصات مضيف نشطة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:225 +msgid "Enabling active service checks.." +msgstr "تفعيل فحوصات الخدمة النشطة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:145 +msgid "Enabling event handler.." +msgstr "تفعيل معالج حدث.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:229 +msgid "Enabling event handlers.." +msgstr "تفعيل معالجات حدث.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:149 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:233 +msgid "Enabling flap detection.." +msgstr "تفعيل كشف التذبذب.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:141 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:237 +msgid "Enabling notifications.." +msgstr "تفعيل التنبيهات.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:241 +msgid "Enabling obsessing over hosts.." +msgstr "تعطيل التوجس على المضيفين.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:245 +msgid "Enabling obsessing over services.." +msgstr "تعطيل التوجس على الخدمات.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:137 +msgid "Enabling obsessing.." +msgstr "تعطيل التوجس.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:133 +msgid "Enabling passive checks.." +msgstr "تفعيل فحوصات سلبية.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:249 +msgid "Enabling passive host checks.." +msgstr "تفعيل فحوصات مضيف سلبية.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:253 +msgid "Enabling passive service checks.." +msgstr "تفعيل فحوصات الخدمة السلبية.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:257 +msgid "Enabling performance data.." +msgstr "تفعيل بيانات الأداء.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:88 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:251 +msgid "End Time" +msgstr "زمن الانتهاء" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:75 +msgid "Ended downtimes" +msgstr "أزمنة التوقف المنتهية" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:159 +msgid "" +"Enter here the duration of the downtime. The downtime will be automatically " +"deleted after this time expired." +msgstr "" +"أدخل وقت التوقف هنا. سيتم حذف زمن التوقف تلقائيا " +"بعد انقضاء هذا الوقت." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:90 +msgid "" +"Enter the expire date and time for this acknowledgement here. Icinga will " +"delete the acknowledgement after this time expired." +msgstr "" +"أدخل تاريخ الانتهاء ووقت هذا الإشعار هنا. \"إسنجا\" سيحذف " +"هذا الإشعار بعد انتهاء هذا الوقت." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:43 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:248 +msgid "Entry Time" +msgstr "زمن الدخول" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:216 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:336 +msgid "Event Grid" +msgstr "شبكة الحدث" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:54 +msgid "Event Handler" +msgstr "معالج الحدث" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:208 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:210 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:108 +msgid "Event Handlers" +msgstr "معالجات الحدث" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:220 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:546 +msgid "Event Overview" +msgstr "نظرة عامة على الحدث" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:116 +msgid "Execution time" +msgstr "وقت التنفيذ" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:441 +msgid "Expiration" +msgstr "انتهاء الصلاحية" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:87 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/DisableNotificationsExpireCommandForm.php:43 +msgid "Expire Time" +msgstr "وقت انتهاء الصلاحية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:57 +msgid "Expires" +msgstr "انتهاء صلاحية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml:27 +#, php-format +msgid "Expires %s" +msgstr "انتهاء صلاحية %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:87 +msgid "FLAPPING" +msgstr "تذبذب" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:91 +msgid "FLAPPING STOPPED" +msgstr "توقف التذبذب" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Command/Transport/CommandTransport.php:138 +msgid "" +"Failed to send external Icinga command. No transport has been configured for " +"this instance. Please contact your Icinga Web administrator." +msgstr "" +"فشل في إرسال أمر \"إسنجا\" الخارجي. لم يتم إعداد نقل " +"لهذه الحالة. يرجى الاتصال بمدير الوب الخاص بإسنجا" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/IdoResourcePage.php:143 +#, php-format +msgid "Failed to successfully validate the configuration: %s" +msgstr "فشل التحقق من صحة الإعداد بنجاح: %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:79 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:200 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:202 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/flags.phtml:2 +msgid "Feature Commands" +msgstr "أوامر ميزة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Navigation/ActionForm.php:24 +msgid "Filter" +msgstr "مرشح" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:60 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:107 +msgid "Fixed" +msgstr "ثابت" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:66 +msgid "Fixed downtimes have a static start and end time." +msgstr "أزمنة التوقف الثابتة لها بداية ونهاية ثابتة." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:8 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:10 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:58 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:117 +msgid "Flap Detection" +msgstr "كشف التذبذب" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml:5 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/HostFlags.php:19 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/ServiceFlags.php:15 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/EventOverviewForm.php:84 +msgid "Flapping" +msgstr "التذبذب" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:60 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:108 +msgid "Flexible" +msgstr "مرن" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:157 +msgid "Flexible Duration" +msgstr "مدة مرنة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:63 +msgid "" +"Flexible downtimes have a hard start and end time, but also an additional " +"restriction on the duration in which the host or service may actually be " +"down." +msgstr "" +"أزمنة التوقف المرنة لديها زمن بداية ونهاية صعب، ولكن أيضا لها تقييد إضافي " +"على المدة التي يكون فيها المضيف أو الخدمة متوقفا." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php:62 +msgid "Force Check" +msgstr "فحص مفروض" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:55 +msgid "Forced" +msgstr "مفروض" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:66 +msgid "From" +msgstr "من" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:48 +msgid "Global Host Event Handler" +msgstr "معالج حدث المضيف العالمي" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:42 +msgid "Global Service Event Handler" +msgstr "معالج حدث الخدمة العالمي" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:31 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:130 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:229 +msgid "Handled" +msgstr "مُعالج" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:55 +msgid "Hard state changes" +msgstr "تغييرات حالة صعبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:325 +msgid "Healing Chart" +msgstr "رسم الصحة البياني" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:89 +msgid "Hide the properties of monitored objects that match the filter" +msgstr "إخفاء خصائص الكائنات المراقبة التي توافق المرشح" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:212 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:69 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php:229 +msgid "History" +msgstr "المحفوظات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:15 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:30 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:29 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:31 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml:20 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/notifications.phtml:53 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:139 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:246 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:438 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:97 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php:185 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/DataView/DataView.php:275 +msgid "Host" +msgstr "مضيف" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:115 +msgid "Host Action" +msgstr "فعل المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:176 +msgid "Host Address" +msgstr "عنوان المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:61 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:121 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:148 +msgid "Host Checks" +msgstr "فحوصات المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hostgroups.phtml:25 +msgid "Host Group" +msgstr "مجموعة المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:291 +msgid "Host Group Chart" +msgstr "رسم بياني لمجموعة المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:534 +msgid "Host Group Name" +msgstr "اسم مجموعة المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:501 +msgid "Host Groups" +msgstr "مجموعات المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:132 +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:268 +msgid "Host Problems" +msgstr "مشاكل المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:35 +msgid "Host Problems:" +msgstr "مشاكل المضيف:" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:173 +msgid "Host Severity" +msgstr "خطورة المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hostgroups.phtml:26 +msgid "Host States" +msgstr "حالات المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:3 +msgid "Host and Service Checks" +msgstr "فحوصات المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/disable-notifications.phtml:10 +msgid "Host and service notifications are already disabled." +msgstr "تنبيهات المضيف والخدمة تم تعطيلها مسبقا." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostController.php:33 +msgid "Host not found" +msgstr "المضيف غير موجود" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:109 +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:188 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/hostgroups.phtml:16 +msgid "Hostgroups" +msgstr "مجموعات المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:89 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:175 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:598 +msgid "Hostname" +msgstr "اسم المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:141 +msgid "Hostname or address of the remote Icinga instance" +msgstr "اسم المضيف وعنوان حالة \"إسنجا\" البعيدة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:107 +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:176 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:9 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:39 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:100 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:295 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostsController.php:59 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:48 +msgid "Hosts" +msgstr "المضيفون" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:639 +msgid "Hour" +msgstr "ساعة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:137 +msgid "Hours" +msgstr "ساعات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/service-header.phtml:30 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/host-header.phtml:29 +msgid "IPv4 address" +msgstr "عنوان Ipv4" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/service-header.phtml:25 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/host-header.phtml:24 +msgid "IPv6 address" +msgstr "عنوان IPv6" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/SecurityStep.php:45 +msgid "" +"Icinga Web 2 will protect your monitoring environment against prying eyes " +"using the configuration specified below:" +msgstr "" +"واجهة وب إسنجا 2 ستحمي بيئة المراقبة الخاصة بك ضد أعين " +"المتطفلين باستخدام الإعداد المحدد أدناه:" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:77 +#, php-format +msgid "" +"Icinga Web 2 will retrieve information from your monitoring environment " +"using a backend called \"%s\" and the specified resource below:" +msgstr "" +"واجهة وب إسنجا 2 ستسترد المعلومات من بيئة المراقبة الخاصة بك " +"باستخدام الخلفية المسماة \"%s\" والمورد المحدد أدناه:" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/TransportStep.php:76 +#, php-format +msgid "" +"Icinga Web 2 will use the named pipe located at \"%s\" to send commands to " +"your monitoring instance." +msgstr "" +"واجهة وب إسنجا 2 ستستخدم الأنبوب المسمى الواقع على \"%s\" لإرسال أوامر " +"إلى مثال المراقبة الخاصة بك." + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/TransportStep.php:49 +#, php-format +msgid "" +"Icinga Web 2 will use the named pipe located on a remote machine at \"%s\" " +"to send commands to your monitoring instance by using the connection details " +"listed below:" +msgstr "" +"واجهة وب إسنجا 2 ستستخدم \"الأنبوب المسمى\" الواقع على الجهاز البعيد \"%s\"" +" لإرسال أوامر " +"إلى مثال المراقبة الخاصة بك باستخدام تفاصيل الاتصال المدونة أدناه:" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:74 +msgid "If the acknowledgement should expire, check this option." +msgstr "إذا كان الإشعار ستنتهي صلاحيته فحدد هذا الخيار." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:58 +msgid "" +"If you check this option, the notification is sent out regardless of time " +"restrictions and whether or not notifications are enabled." +msgstr "" +"إذا قمت بتحديد هذا الخيار، فإن التنبيه سيرسل بغض النظر عن تقييدات " +"الوقت وكون التنبيهات مفعلة أم لا." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:70 +msgid "" +"If you check this option, the notification is sent out to all normal and " +"escalated contacts." +msgstr "" +"إذا قمت بتحديد هذا الخيار، فإن التنبيه سيرسل لكل جهات " +"الاتصال العادية والمصعّدة." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:126 +msgid "" +"If you do not want an acknowledgement notification to be sent out to the " +"appropriate contacts, uncheck this option." +msgstr "" +"إذا كنت تريد لتنبيه الإشعار أن يرسل لجهة الاتصال المناسبة " +"فقم بإلغاء هذا الخيار." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:101 +msgid "" +"If you select the fixed option, the downtime will be in effect between the " +"start and end times you specify whereas a flexible downtime starts when the " +"host or service enters a problem state sometime between the start and end " +"times you specified and lasts as long as the duration time you enter. The " +"duration fields do not apply for fixed downtimes." +msgstr "" +"إذا قمت بتحديد الخيار الثابت، فإن زمن التوقف يكون ساري المفعول بين" +"أزمنة البداية والنهاية التي تحددها في حين يبدأ التوقف المرن عندما يدخل" +"مضيف أو خدمة إلى حالة مشكلة في وقت ما بين زمن البداية والنهاية " +"الذين حددتهما ويستمر طيلة المدة الزمنية التي تدخلها. حقول الزمن " +"لا تنطبق على أزمنة التوقف الثابتة." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php:64 +msgid "" +"If you select this option, Icinga will force a check regardless of both what " +"time the scheduled check occurs and whether or not checks are enabled." +msgstr "" +"إذا قمت بتحديد هذا الخيار، فإن إسنجا ستفرض اختبارا بغض النظر عن" +"زمن حدوث الاختبار المجدول وعما إذا كانت الاختبارات مفعلة." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:175 +msgid "" +"If you still have any environments or views referring to this transport, you " +"won't be able to send commands anymore after deletion." +msgstr "" +"إذا كان لديك أي بيئات أو عروض تشير إلى هذا النقل، فلن " +"لن تكون قادرا على إرسال الأوامر مرة أخرى بعد الحذف." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php:58 +msgid "" +"If you uncheck this option, the comment will automatically be deleted the " +"next time Icinga is restarted." +msgstr "" +"إذا قمت بإلغاء تحديد هذا الخيار، سيتم تلقائيا حذف التعليق" +"في المرة القادمة التي سيعاد فيها تشغيل إسنجا." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:114 +msgid "" +"If you want the acknowledgement to remain until the host or service recovers " +"even if the host or service changes state, check this option." +msgstr "" +"ذا كنت ترغب في بقاء الإشعار حتى يتعافى المضيف أو الخدمة" +"حتى إذا تغيرت حالة المضيف أو الخدمة، فاختر هذا الخيار." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php:44 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:45 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:67 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:51 +msgid "" +"If you work with other administrators, you may find it useful to share " +"information about the the host or service that is having problems. Make sure " +"you enter a brief description of what you are doing." +msgstr "" +"إذا كنت تعمل مع المسؤولين الآخرين، قد تجد أنه من المفيد تبادل " +"المعلومات حول المضيف أو الخدمة التي تواجه مشاكل. تأكد " +"من إدخال وصفا موجزا لما تقوم به." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:63 +msgid "" +"If you would like the comment to remain even when the acknowledgement is " +"removed, check this option." +msgstr "" +"إذا كنت ترغب في بقاء التعليق حتى عندما يُزال الإقرار " +"فاختر هذا الخيار." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/service/statusicons.phtml:17 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/host/statusicons.phtml:17 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/HostFlags.php:25 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/ServiceFlags.php:21 +msgid "In Downtime" +msgstr "في زمن التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:151 +msgid "In effect" +msgstr "ساري المفعول" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:131 +msgid "" +"Indicates the number of seconds that the scheduled downtime should last. " +"This is usually only needed if this is a flexible downtime, which can start " +"at a variable time, but lasts for the specified duration" +msgstr "" +"يشير إلى عدد الثواني التي يستمرها زمن التوقف المجدول. عادة " +"ما تكون هناك حاجة إليها إذا كان هذا هو وقت التوقف المرن، " +"الذي يمكن أن يبدأ في وقت متغير، ولكن يستمر للمدة المحددة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/command.phtml:7 +msgid "Instance" +msgstr "حالة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:203 +msgid "Instance Link" +msgstr "رابط الحالة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:84 +#, php-format +msgid "Invalid command transport type \"%s\" given" +msgstr "نوع ناقل الأمر المعطى \"%s\" غير صالح " + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Navigation/ActionForm.php:61 +#, php-format +msgid "Invalid filter provided. You can only use the following columns: %s" +msgstr "تم تزويد مرشح غير صالح. تستطيع استخدام الأعمدة التالية فقط: %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:245 +msgid "Is In Effect" +msgstr "ساري المفعول" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checksource.phtml:11 +msgid "Is reachable" +msgstr "يمكن الوصول إليه" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/WelcomePage.php:43 +msgid "" +"It offers various status and reporting views with powerful filter " +"capabilities that allow you to keep track of the most important events in " +"your monitoring environment." +msgstr "" +"ويقدم الوضع عروض مختلفة للحالة والتقارير مع قدرات تصفية قوية " +"تسمح لك بتتبع أهم الأحداث في بيئة المراقبة الخاصة بك." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/Perfdata.php:36 +msgid "Label" +msgstr "عنوان" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:91 +msgid "Last Check" +msgstr "آخر فحص" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:32 +msgid "Last External Command Check" +msgstr "آخر فحص أمر خارجي" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:177 +msgid "Last Host Check" +msgstr "آخر فحص لمضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:36 +msgid "Last Log File Rotation" +msgstr "آخر تدوير لملف السحل" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:171 +msgid "Last Service Check" +msgstr "آخر فحص لخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:92 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:172 +msgid "Last State Change" +msgstr "آخر تغيير حالة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:28 +msgid "Last Status Update" +msgstr "آخر تحديث للحالة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:7 +msgid "Last check" +msgstr "آخر فحص" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:7 +msgid "Last update" +msgstr "آخر تحديث" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:294 +msgid "Late Host Check Results" +msgstr "آخر نتائج لفحص المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:298 +msgid "Late Service Check Results" +msgstr "آخر نتائج لفحص الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:115 +msgid "Latency" +msgstr "تأخير" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/timeline/index.phtml:15 +msgid "Legend" +msgstr "الدليل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notes.phtml:23 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notes.phtml:24 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/actions.phtml:17 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/actions.phtml:18 +msgid "Link opens in new window" +msgstr "يفتح الرابط في نافذة جديدة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hosts.phtml:75 +#, php-format +msgid "List %s unhandled service problem on host %s" +msgid_plural "List %s unhandled service problems on host %s" +msgstr[0] "سرد مشكلة الخدمة غير المعالجة %s على المضيف %s" +msgstr[1] "سرد مشاكل الخدمة غير المعالجة %s على المضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/timeline/index.phtml:112 +#, php-format +msgctxt "timeline.link.title" +msgid "List %u %s registered %s" +msgstr "سرد %u %s مسجلة %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:26 +#, php-format +msgid "List %u actively checked host" +msgid_plural "List %u actively checked hosts" +msgstr[0] "سرد %u مضيف مفحوص بطريقة نشطة" +msgstr[1] "سرد %u من المضيفين المفحوصين بطريقة نشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:82 +#, php-format +msgid "List %u actively checked service" +msgid_plural "List %u actively checked services" +msgstr[0] "سرد الخدمة %u المفحوصة بطريقة نشطة" +msgstr[1] "سرد الخدمات %u المفحوصة بطريقة نشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/components/hostssummary.phtml:17 +#, php-format +msgid "List %u host" +msgid_plural "List all %u hosts" +msgstr[0] "سرد %u مضيف" +msgstr[1] "سرد %u من المضيفين" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:29 +#, php-format +msgid "List %u host for which flap detection has been disabled" +msgid_plural "List %u hosts for which flap detection has been disabled" +msgstr[0] "سرد %u مضيف عطل فيه كشف التذبذب" +msgstr[1] "سرد %u من المضيفين الذين عطل فيهم كشف التذبذب" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:150 +#, php-format +msgid "List %u host for which notifications are suppressed" +msgid_plural "List %u hosts for which notifications are suppressed" +msgstr[0] "سرد %u مضيف حيث أُخمدت التنبيهات" +msgstr[1] "سرد %u من المضيفين حيث أُخمدت التنبيهات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:59 +#, php-format +msgid "List %u host that is currently flapping" +msgid_plural "List %u hosts which are currently flapping" +msgstr[0] "سرد %u مضيف يتذبذب حاليا" +msgstr[1] "سرد %u مضيف يتذبذب حاليا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/problem_hosts.phtml:18 +#, php-format +msgid "List %u host that is currently in state DOWN" +msgid_plural "List %u hosts which are currently in state DOWN" +msgstr[0] "سرد %u مضيف حاليا في حالة \"متوقف\"" +msgstr[1] "سرد %u من المضيفين حاليا في حالة \"متوقف\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/ok_hosts.phtml:41 +#, php-format +msgid "List %u host that is currently in state PENDING" +msgid_plural "List %u hosts which are currently in state PENDING" +msgstr[0] "سرد %u مضيف حاليا في حالة \"معلق\"" +msgstr[1] "سرد %u من المضيفين حاليا في حالة \"معلق\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/problem_hosts.phtml:40 +#, php-format +msgid "List %u host that is currently in state UNREACHABLE" +msgid_plural "List %u hosts which are currently in state UNREACHABLE" +msgstr[0] "سرد %u مضيف حاليا في حالة \"لا يمكن الوصول\"" +msgstr[1] "سرد %u من المضيفين حاليا في حالة \"لا يمكن الوصول\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/ok_hosts.phtml:23 +#, php-format +msgid "List %u host that is currently in state UP" +msgid_plural "List %u hosts which are currently in state UP" +msgstr[0] "سرد %u مضيف حاليا في حالة \"سليم\"" +msgstr[1] "سرد %u من المضيفين حاليا في حالة \"سليمة\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:62 +#, php-format +msgid "List %u host that is not being checked at all" +msgid_plural "List %u hosts which are not being checked at all" +msgstr[0] "سرد %u مضيف لم يتم فحصه مطلقا" +msgstr[1] "سرد %u من المضيفين لم يتم فحصها مطلقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:229 +#, php-format +msgid "List %u host that is not processing any event handlers" +msgid_plural "List %u hosts which are not processing any event handlers" +msgstr[0] "سرد %u مضيف لا تقوم بمعالجة أي معالجات حدث" +msgstr[1] "سرد %u من المضيفين لا تقوم بمعالجة أي معالجات حدث" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:44 +#, php-format +msgid "List %u passively checked host" +msgid_plural "List %u passively checked hosts" +msgstr[0] "سرد %u مضيف مفحوص بطريقة سالبة" +msgstr[1] "سرد %u من المضيفين المفحوصين بطريقة سالبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:100 +#, php-format +msgid "List %u passively checked service" +msgid_plural "List %u passively checked services" +msgstr[0] "سرد %u خدمة مفحوصة بطريقة سالبة" +msgstr[1] "سرد %u من الخدمات المفحوصة بطريقة سالبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/components/servicesummary.phtml:20 +#, php-format +msgid "List %u service" +msgid_plural "List all %u services" +msgstr[0] "سرد %u خدمة" +msgstr[1] "سرد %u من الخدمات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:84 +#, php-format +msgid "List %u service for which flap detection has been disabled" +msgid_plural "List %u services for which flap detection has been disabled" +msgstr[0] "سرد %u خدمة عطل فيه كشف التذبذب" +msgstr[1] "سرد %u من الخدمات التي عطل فيها كشف التذبذب" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:184 +#, php-format +msgid "List %u service for which notifications are suppressed" +msgid_plural "List %u services for which notifications are suppressed" +msgstr[0] "سرد %u خدمة حيث أُخمدت التنبيهات" +msgstr[1] "سرد %u من الخدمات حيث أُخمدت التنبيهات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:114 +#, php-format +msgid "List %u service that is currently flapping" +msgid_plural "List %u services which are currently flapping" +msgstr[0] "سرد %u خدمة مذبذبة حاليا" +msgstr[1] "سرد %u من الخدمات مذبذبة حاليا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:20 +#, php-format +msgid "List %u service that is currently in state CRITICAL" +msgid_plural "List %u services which are currently in state CRITICAL" +msgstr[0] "سرد %u خدمة حاليا في حالة \"ضرورية\"" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"ضرورية\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:41 +#, php-format +msgid "List %u service that is currently in state CRITICAL (Handled)" +msgid_plural "List %u services which are currently in state CRITICAL (Handled)" +msgstr[0] "سرد %u خدمة حاليا في حالة \"ضرورية\" (مُعالجة)" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"ضرورية\" (مُعالجة)" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:95 +#, php-format +msgid "" +"List %u service that is currently in state CRITICAL and not checked at all" +msgid_plural "" +"List %u services which are currently in state CRITICAL and not checked at all" +msgstr[0] "سرد %u خدمة حاليا في حالة \"ضرورية\" ولم يتم فحصها مطلقا" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"ضرورية\" ولم يتم فحصها مطلقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:68 +#, php-format +msgid "" +"List %u service that is currently in state CRITICAL and passively checked" +msgid_plural "" +"List %u services which are currently in state CRITICAL and passively checked" +msgstr[0] "سرد %u خدمة حاليا في حالة \"ضرورية\" وتم فحصها سلبيا" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"ضرورية\" وتم فحصها سلبيا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:314 +#, php-format +msgid "List %u service that is currently in state OK" +msgid_plural "List %u services which are currently in state OK" +msgstr[0] "سرد %u خدمة حاليا في حالة \"سليمة\"" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"سليمة\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:340 +#, php-format +msgid "List %u service that is currently in state OK and not checked at all" +msgid_plural "" +"List %u services which are currently in state OK and not checked at all" +msgstr[0] "سرد %u خدمة حاليا في حالة \"سليمة\" ولم يتم فحصها مطلقا" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"سليمة\" ولم يتم فحصها مطلقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:361 +#, php-format +msgid "List %u service that is currently in state PENDING" +msgid_plural "List %u services which are currently in state PENDING" +msgstr[0] "سرد %u خدمة حاليا في حالة \"معلقة\"" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"معلقة\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:387 +#, php-format +msgid "" +"List %u service that is currently in state PENDING and not checked at all" +msgid_plural "" +"List %u services which are currently in state PENDING and not checked at all" +msgstr[0] "سرد %u خدمة حاليا في حالة \"معلقة\" ولم يتم فحصها مطلقا" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"معلقة\" ولم يتم فحصها مطلقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:218 +#, php-format +msgid "List %u service that is currently in state UNKNOWN" +msgid_plural "List %u services which are currently in state UNKNOWN" +msgstr[0] "سرد %u خدمة حاليا في حالة \"مجهولة\"" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"مجهولة\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:239 +#, php-format +msgid "List %u service that is currently in state UNKNOWN (Handled)" +msgid_plural "List %u services which are currently in state UNKNOWN (Handled)" +msgstr[0] "سرد %u خدمة حاليا في حالة \"مجهولة\" (معالجة)" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"مجهولة\" (مُعالجة)" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:293 +#, php-format +msgid "" +"List %u service that is currently in state UNKNOWN and not checked at all" +msgid_plural "" +"List %u services which are currently in state UNKNOWN and not checked at all" +msgstr[0] "سرد %u خدمة حاليا في حالة \"مجهولة\" ولم يتم فحصها مطلقا" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"مجهولة\" ولم يتم فحصها مطلقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:266 +#, php-format +msgid "" +"List %u service that is currently in state UNKNOWN and passively checked" +msgid_plural "" +"List %u services which are currently in state UNKNOWN and passively checked" +msgstr[0] "سرد %u خدمة حاليا في حالة \"مجهولة\" وتم فحصها سلبيا" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"مجهولة\" وتم فحصها سلبيا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:119 +#, php-format +msgid "List %u service that is currently in state WARNING" +msgid_plural "List %u services which are currently in state WARNING" +msgstr[0] "سرد %u خدمة حاليا في حالة \"تحذير\"" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"تحذير\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:140 +#, php-format +msgid "List %u service that is currently in state WARNING (Handled)" +msgid_plural "List %u services which are currently in state WARNING (Handled)" +msgstr[0] "سرد %u خدمة حاليا في حالة \"تحذير\" (مُعالجة)" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"تحذير\" (مُعالجة)" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:194 +#, php-format +msgid "" +"List %u service that is currently in state WARNING and not checked at all" +msgid_plural "" +"List %u services which are currently in state WARNING and not checked at all" +msgstr[0] "سرد %u خدمة حاليا في حالة \"تحذير\" ولم يتم فحصها مطلقا" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"تحذير\" ولم يتم فحصها مطلقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:167 +#, php-format +msgid "" +"List %u service that is currently in state WARNING and passively checked" +msgid_plural "" +"List %u services which are currently in state WARNING and passively checked" +msgstr[0] "سرد %u خدمة حاليا في حالة \"تحذير\" وتم فحصها سلبيا" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"تحذير\" وتم فحصها سلبيا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:118 +#, php-format +msgid "List %u service that is not being checked at all" +msgid_plural "List %u services which are not being checked at all" +msgstr[0] "سرد %u خدمة لم يتم فحصها مطلقا" +msgstr[1] "سرد %u من الخدمات التي لم يتم فحصها مطلقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:263 +#, php-format +msgid "List %u service that is not processing any event handlers" +msgid_plural "List %u services which are not processing any event handlers" +msgstr[0] "سرد %u خدمة لا تقوم بمعالجة أي معالجات حدث" +msgstr[1] "سرد %u من الخدمات التي لا تقوم بمعالجة أي معالجات حدث" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comments-header.phtml:23 +#, php-format +msgid "List all %d comments" +msgstr "سرد جميع التعليقات %d" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtimes-header.phtml:31 +#, php-format +msgid "List all %d downtimes" +msgstr "سرد جميع أزمنة التوقف %d" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/host/objects-header.phtml:32 +#, php-format +msgid "List all %d hosts" +msgstr "سرد جميع المضيفين %d" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/service/objects-header.phtml:36 +#, php-format +msgid "List all %d services" +msgstr "سرد جميع الخدمات %d" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/timeline/index.phtml:74 +#, php-format +msgctxt "timeline.link.title" +msgid "List all event records registered %s" +msgstr "سرد جميع سجلات الأحداث المسجلة %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/hostgroups.phtml:11 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hostgroups.phtml:43 +#, php-format +msgid "List all hosts in the group \"%s\"" +msgstr "سرد جميع المضيفين في مجموعة \"%s\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:43 +msgid "List all hosts, for which flap detection is enabled entirely" +msgstr "سرد جميع المضيفين الذين فُعل لهم كشف التذبذب بصورة كاملة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:164 +msgid "List all hosts, for which notifications are enabled entirely" +msgstr "سرد جميع المضيفين الذين فُعلت لهم التنبيهات بصورة كاملة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:243 +msgid "List all hosts, which are processing event handlers entirely" +msgstr "سرد جميع المضيفين الذين يقومون بمعالجة معالجات الحدث بصورة كاملة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegrid.phtml:56 +#, php-format +msgid "List all reported services on host %s" +msgstr "سرد جميع الخدمات المذكورة على المضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/servicegroups.phtml:11 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegroups.phtml:38 +#, php-format +msgid "List all services in the group \"%s\"" +msgstr "سرد جميع الخدمات في مجموعة \"%s\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hostgroups.phtml:141 +#, php-format +msgid "List all services of all hosts in host group \"%s\"" +msgstr "سرد جميع الخدمات لكل المضيفين في مجموعة المضيف \"%s\"" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php:209 +#, php-format +msgid "List all services on host %s" +msgstr "سرد جميع الخدمات على المضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegrid.phtml:36 +#, php-format +msgid "List all services with the name \"%s\" on all reported hosts" +msgstr "سرد جميع الخدمات باسم \"%s\" على جميع المضيفين المذكورين" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:98 +msgid "List all services, for which flap detection is enabled entirely" +msgstr "سرد جميع الخدمات اللاتي فُعّل لها كشف التذبذب بصورة كاملة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:198 +msgid "List all services, for which notifications are enabled entirely" +msgstr "سرد جميع الخدمات اللاتي فُعّلت لها التنبيهات بصورة كاملة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:277 +msgid "List all services, which are processing event handlers entirely" +msgstr "سرد جميع الخدمات اللاتي تُعالج معالجات الحدث بصورة كاملة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:411 +msgid "List comments" +msgstr "سرد التعليقات" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:382 +msgid "List contact groups" +msgstr "سرد مجموعات جهة الاتصال" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:308 +msgid "List contacts" +msgstr "سرد جهات الاتصال" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/contacts.phtml:29 +#, php-format +msgid "List contacts in contact-group \"%s\"" +msgstr "سرد جهات الاتصال في مجموعة الاتصال \"%s\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:213 +msgid "List downtimes" +msgstr "سرد أزمنة التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:547 +msgid "List event records" +msgstr "سرد سجلات الحدث" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:502 +msgid "List host groups" +msgstr "سرد مجموعات المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:49 +msgid "List hosts" +msgstr "سرد المضيفين" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:273 +msgid "List notifications" +msgstr "سرد التنبيهات" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:462 +msgid "List service groups" +msgstr "سرد مجموعات الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:124 +msgid "List services" +msgstr "سرد الخدمات" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:119 +msgid "Livestatus Resource" +msgstr "مورد Livestatus" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:183 +msgid "Load More" +msgstr "تحميل أكثر" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegrid.phtml:93 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegrid.phtml:114 +msgid "Load more" +msgstr "تحميل أكثر" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:95 +msgid "Local" +msgstr "محلي" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:225 +msgid "Local Command File" +msgstr "ملف أمر محلي" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:89 +msgid "Make use of the ssh identity resource" +msgstr "استخدم مورد الهوية ssh" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/Perfdata.php:39 +msgid "Max" +msgstr "أقصى" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:444 +msgid "Max (min)" +msgstr "أقصى (أدنى)" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/Perfdata.php:38 +msgid "Min" +msgstr "أدنى" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:147 +msgid "Minutes" +msgstr "دقائق" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:21 +msgid "Monitoring Backend" +msgstr "مراقبة الخلفية" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/BackendPage.php:14 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:73 +msgctxt "setup.page.title" +msgid "Monitoring Backend" +msgstr "مراقبة الخلفية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:7 +msgid "Monitoring Backends" +msgstr "مراقبة الخلفيات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:3 +msgid "Monitoring Features" +msgstr "مراقبة المميزات" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:249 +msgid "Monitoring Health" +msgstr "مراقبة الصحة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/IdoResourcePage.php:21 +msgctxt "setup.page.title" +msgid "Monitoring IDO Resource" +msgstr "مراقبة مورد IDO" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/LivestatusResourcePage.php:14 +msgctxt "setup.page.title" +msgid "Monitoring Livestatus Resource" +msgstr "مراقبة مورد Livestatus" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/SecurityPage.php:14 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/SecurityStep.php:42 +msgctxt "setup.page.title" +msgid "Monitoring Security" +msgstr "مراقبة الأمان" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:79 +#, php-format +msgid "Monitoring backend \"%s\" not found" +msgstr "مراقبة الخلفية \"%s\" غير موجود" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:149 +#, php-format +msgid "Monitoring backend \"%s\" successfully removed" +msgstr "تمت إزالة مراقبة الخلفية \"%s\" بنجاح" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:68 +#, php-format +msgid "Monitoring backend \"%s\" successfully updated" +msgstr "تم تحديث مراقبة الخلفية \"%s\" بنجاح" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Navigation/Renderer/BackendAvailabilityNavigationItemRenderer.php:66 +#, php-format +msgid "Monitoring backend %s is not running" +msgstr "الخلفية %s لا تعمل" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:151 +#, php-format +msgid "" +"Monitoring backend configuration could not be written to: %s. An error " +"occured:" +msgstr "إعداد خلفية المراقبة لم يُتمكن من كتابته إلى: %s. حدث خطأ:" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:144 +#, php-format +msgid "Monitoring backend configuration has been successfully written to: %s" +msgstr "إعداد خلفية المراقبة تمت كتابته إلى: %s بنجاح" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:116 +msgid "Monitoring backend successfully created" +msgstr "تم إنشاء خلفية المراقبة بنجاح" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/SecurityStep.php:76 +#, php-format +msgid "" +"Monitoring security configuration could not be written to: %s. An error " +"occured:" +msgstr "إعداد مراقبة الأمان لم يُتمكن من كتابته إلى: %s. حدث خطأ:" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/SecurityStep.php:68 +#, php-format +msgid "Monitoring security configuration has been successfully created: %s" +msgstr "إعداد مراقبة الأمان تم إنشاؤه بنجاح: %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:648 +msgid "Month" +msgstr "شهر" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:313 +msgid "Muted" +msgstr "صامت" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:21 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:39 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:45 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:51 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:57 +msgid "N/A" +msgstr "غير متوفر" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:49 +msgid "NOTIFICATION" +msgstr "تنبيه" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contacts.phtml:20 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:325 +msgid "Name" +msgstr "اسم" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/SecurityConfigForm.php:31 +msgid "New security configuration has successfully been stored" +msgstr "تم تخزين إعداد الأمان الجديد بنجاح" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:22 +msgid "Next check" +msgstr "الفحص التالي" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:22 +msgid "Next update" +msgstr "التحديث التالي" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:155 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:48 +msgid "No" +msgstr "لا" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php:176 +msgid "No backend has been configured" +msgstr "لم يتمّ تهيئة أيّة خلفية" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Command/Transport/CommandTransport.php:41 +#, php-format +msgid "No command transports have been configured in \"%s\"." +msgstr "لم يتمّ تهيئة أيّ أمر نواقل في: \"%s\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/comments.phtml:15 +msgid "No comments found matching the filter" +msgstr "لا توجد تعليقات توافق المرشح" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php:189 +#, php-format +msgid "No configuration for backend %s" +msgstr "لا يوجد إعداد للخلفية %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contactgroups.phtml:16 +msgid "No contact groups found matching the filter" +msgstr "لا توجد مجموعات توافق المرشح" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contacts.phtml:14 +msgid "No contacts found matching the filter" +msgstr "لا توجد جهات اتصال توافق المرشح" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/downtimes.phtml:19 +msgid "No downtimes found matching the filter." +msgstr "لا يوجد أزمنة توقف توافق المرشح." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:31 +msgid "No historical events found matching the filter." +msgstr "لا توجد أحداث تاريخية توافق المرشح." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hostgroups.phtml:18 +msgid "No host groups found matching the filter." +msgstr "لا توجد مجموعات مضيف توافق المرشح." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:18 +msgid "No hosts found matching the filter" +msgstr "لا يوجد مضيفين يوافقون المرشح" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hosts.phtml:19 +msgid "No hosts found matching the filter." +msgstr "لا يوجد مضيفين يوافقون المرشح." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notifications.phtml:62 +msgid "No notification has been sent for this issue." +msgstr "لم تُرسَل أي تنبيهات لهذه المشكلة." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/notifications.phtml:18 +msgid "No notifications found matching the filter." +msgstr "لا يوجد تنبيهات توافق المرشح." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:69 +msgid "No notifications have been sent for this contact" +msgstr "لم تُرسَل أي تنبيهات لجهة الاتصال هذه" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegroups.phtml:16 +msgid "No service groups found matching the filter." +msgstr "لا توجد مجموعات خدمة توافق المرشح." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:20 +msgid "No services found matching the filter" +msgstr "لا توجد خدمات توافق المرشح" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/services.phtml:20 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegrid.phtml:14 +msgid "No services found matching the filter." +msgstr "لا توجد خدمات توافق المرشح." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/eventgrid.phtml:77 +msgid "No state changes in the selected time period." +msgstr "ليس هناك تغييرات حالة في الفترة الزمنية المحددة." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:12 +msgid "No such contact" +msgstr "جهة الاتصال غير موجودة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:198 +msgctxt "command transport instance association" +msgid "None" +msgstr "لا شيء" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/SecurityStep.php:56 +msgctxt "monitoring.protected_customvars" +msgid "None" +msgstr "لا شيء" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml:57 +msgid "Not acknowledged" +msgstr "غير معترف به" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checksource.phtml:13 +msgid "Not reachable" +msgstr "لا يمكن الوصول إليه" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/notifications.phtml:68 +msgid "Not sent out to any contact" +msgstr "لم يتم إرساله لأي جهة اتصال" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notes.phtml:43 +msgid "Notes" +msgstr "ملاحظات" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:294 +msgid "Notification Start" +msgstr "بداية التنبيه" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:224 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:129 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:131 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:137 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:141 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/detail-content.phtml:24 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:138 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:143 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notifications.phtml:2 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/EventOverviewForm.php:74 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:50 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:126 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:329 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:424 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:467 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:474 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:50 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:272 +msgid "Notifications" +msgstr "تنبيهات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/service/statusicons.phtml:13 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/host/statusicons.phtml:13 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/HostFlags.php:22 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/ServiceFlags.php:18 +msgid "Notifications Disabled" +msgstr "التنبيهات معطلة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:15 +msgid "Notifications and Problems" +msgstr "التنبيهات والمشاكل" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:326 +msgid "Notifications and average reaction time per hour." +msgstr "التنبيهات ومتوسط زمن رد الفعل في الساعة." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:464 +msgid "Notifications and defects per hour" +msgstr "التنبيهات والعيوب في الساعة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:58 +msgid "Notifications sent to this contact" +msgstr "التنبيهات المرسلة لجهة الاتصال" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:78 +#, php-format +msgid "Notifications will be re-enabled in %s" +msgstr "سيعاد تفعيل التنبيهات في %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/disable-notifications.phtml:13 +#, php-format +msgid "Notifications will be re-enabled in %s." +msgstr "سيعاد تفعيل التنبيهات في %s." + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/Service.php:190 +msgid "OK" +msgstr "سليم" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:56 +msgctxt "icinga.state" +msgid "OK" +msgstr "سليم" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:96 +msgid "Object type" +msgstr "نوع الكائن" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:46 +msgid "Obsessing" +msgstr "توجس" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:149 +msgid "Obsessing Over Hosts" +msgstr "توجس على المضيفين" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:158 +msgid "Obsessing Over Services" +msgstr "توجس على الخدمات" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:568 +msgid "Occurence" +msgstr "حدوث" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:120 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:243 +msgid "Ok" +msgstr "سليم" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:535 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:104 +msgid "One day" +msgstr "يوم واحد" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:537 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:106 +msgid "One month" +msgstr "شهر واحد" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:536 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:105 +msgid "One week" +msgstr "أسبوع واحد" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:538 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:107 +msgid "One year" +msgstr "سنة واحدة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:68 +msgid "Output" +msgstr "مخرج" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:292 +msgid "Overdue" +msgstr "متأخر" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:168 +msgid "Overview" +msgstr "نظرة عامة" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/Host.php:191 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/Service.php:202 +msgid "PENDING" +msgstr "معلق" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:34 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contacts.phtml:22 +msgid "Pager" +msgstr "جهاز إخطار" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:328 +msgid "Pager Address / Number" +msgstr "عنوان / رقم جهاز الإخطار" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:141 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:42 +msgid "Passive Checks" +msgstr "اختبارات سلبية" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:167 +msgid "Passive Host Checks" +msgstr "فحوصات مضيف سلبية" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:176 +msgid "Passive Service Checks" +msgstr "فحوصات الخدمة السلبية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:136 +msgid "Passive checks" +msgstr "فحوصات سلبية" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:113 +msgid "Password" +msgstr "كلمة مرور" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:179 +msgid "Path to the Icinga command file on the remote Icinga instance" +msgstr "مسار ملف أمر إسنجا على حالة إسنجا البعيدة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/LocalTransportForm.php:32 +msgid "Path to the local Icinga command file" +msgstr "مسار ملف أمر إسنجا المحلي" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:88 +msgid "Per Host" +msgstr "لكل مضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:77 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:187 +msgid "Performance Data" +msgstr "بيانات الأداء" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/perfdata.phtml:2 +msgid "Performance data" +msgstr "بيانات الأداء" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:47 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php:55 +msgid "Persistent" +msgstr "دائم" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:61 +msgid "Persistent Comment" +msgstr "تعليق دائم" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/BackendPage.php:16 +msgid "" +"Please configure below how Icinga Web 2 should retrieve monitoring " +"information." +msgstr "الرجاء إعداد كيفية استعادة معلومات المراقبة من قبل إسنجا وب 2" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/TransportPage.php:16 +msgid "" +"Please define below how you want to send commands to your monitoring " +"instance." +msgstr "الرجاء تعريف كيف تريد إرسال أوامر لحالة المراقبة خاصتك." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/IdoResourcePage.php:23 +msgid "" +"Please fill out the connection details below to access the IDO database of " +"your monitoring environment." +msgstr "الرجاء تعبئة تفاصيل الاتصال أدناه للوصول لقاعدة بيانات بيئة المراقبة." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/LivestatusResourcePage.php:16 +msgid "" +"Please fill out the connection details below to access the Livestatus socket " +"interface for your monitoring environment." +msgstr "" +"الرجاء تعبئة تفاصيل الاتصال أدناه للوصول لواجهة مقبس Livestatus لبيئة" +" المراقبة خاصتك." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/output.phtml:1 +msgid "Plugin Output" +msgstr "مخرج التوصيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:150 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:101 +msgid "Port" +msgstr "المنفذ" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/components/selectioninfo.phtml:3 +msgctxt "Multi-selection help" +msgid "" +"Press and hold the Ctrl key while clicking on rows to select multiple rows " +"or press and hold the Shift key to select a range of rows" +msgstr "" +"اضغط باستمرار على مفتاح Ctrl أثناء الضغط على صفوف لاختيار صفوف متعددة " +"أو الضغط باستمرار مع مفتاح Shift لاختيار مدى من الصفوف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:20 +msgid "Problem Handling" +msgstr "معالجة المشكلة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/detail-content.phtml:5 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:22 +msgid "Problem handling" +msgstr "معالجة المشكلة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:124 +msgid "Problems" +msgstr "المشاكل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:14 +msgid "Process Info" +msgstr "معلومات العملية" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HealthController.php:33 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HealthController.php:55 +msgid "Process Information" +msgstr "معلومات العملية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:166 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:168 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/command.phtml:21 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/command.phtml:33 +msgid "Process check result" +msgstr "معالجة نتيجة الفحص" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:107 +msgid "Processing check result.." +msgid_plural "Processing check results.." +msgstr[0] "معالجة نتيجة الفحص.." +msgstr[1] "معالجة نتائج الفحص.." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:24 +msgid "Program Start Time" +msgstr "زمن بداية البرنامج" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:18 +msgid "Program Version" +msgstr "إصدار البرنامج" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/SecurityConfigForm.php:56 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/SecurityStep.php:53 +msgid "Protected Custom Variables" +msgstr "متغيرات مخصصة محمية" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:264 +msgid "Recently Recovered Services" +msgstr "خدمات تعافت مؤخرا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:95 +msgid "Remote" +msgstr "بعيد" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:226 +msgid "Remote Command File" +msgstr "ملف أمر بعيد" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/TransportStep.php:59 +msgid "Remote Host" +msgstr "مضيف بعيد" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/TransportStep.php:63 +msgid "Remote SSH Port" +msgstr "منفذ SSH بعيد" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/TransportStep.php:67 +msgid "Remote SSH User" +msgstr "مستخدم SSH بعيد" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/DeleteDowntimesCommandForm.php:63 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/DeleteCommentsCommandForm.php:63 +msgid "Remove" +msgid_plural "Remove All" +msgstr[0] "إزالة" +msgstr[1] "إزالة الكل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comments/show.phtml:11 +#, php-format +msgid "Remove %d comments" +msgstr "إزالة %d من التعليقات" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:172 +#, php-format +msgid "Remove Command Transport %s" +msgstr "إزالة ناقل أمر %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:139 +#, php-format +msgid "Remove Monitoring Backend %s" +msgstr "إزالة خلفية مراقبة %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtimes/show.phtml:11 +#, php-format +msgid "Remove all %d scheduled downtimes" +msgstr "إزالة كل أزمنة المجدولة %d" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/CommentsController.php:93 +msgid "Remove all Comments" +msgstr "إزالة كل التعليقات" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/DowntimesController.php:97 +msgid "Remove all Downtimes" +msgstr "إزالة كل أزمنة التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comments/show.phtml:16 +msgid "Remove all selected comments" +msgstr "إزالة كل التعليقات المحددة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtimes/show.phtml:16 +msgid "Remove all selected downtimes" +msgstr "إزالة كل أزمنة التوقف المحددة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:107 +#, php-format +msgid "Remove command transport %s" +msgstr "إزالة ناقل الأمر %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:52 +#, php-format +msgid "Remove monitoring backend %s" +msgstr "إزالة خلفية المراقبة %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/RemoveAcknowledgementCommandForm.php:73 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/RemoveAcknowledgementCommandForm.php:92 +msgid "Remove problem acknowledgement" +msgid_plural "Remove problem acknowledgements" +msgstr[0] "إزالة إشعار مشكلة" +msgstr[1] "إزالة إشعارات مشكلة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/RemoveAcknowledgementCommandForm.php:114 +msgid "Removing problem acknowledgement.." +msgid_plural "Removing problem acknowledgements.." +msgstr[0] "إزالة إشعار مشكلة.." +msgstr[1] "إزالة إشعارات مشكلة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:540 +msgid "Report interval" +msgstr "فترة التقرير" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:236 +msgid "Reporting" +msgstr "التقارير" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:188 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:190 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:34 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:48 +msgid "Reschedule" +msgstr "إعادة الجدولة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostController.php:142 +msgid "Reschedule Host Check" +msgstr "إعادة جدولة فحص المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostsController.php:187 +msgid "Reschedule Host Checks" +msgstr "إعادة جدولة فحص المضيفين" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServiceController.php:100 +msgid "Reschedule Service Check" +msgstr "إعادة جدولة فحص الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServicesController.php:190 +msgid "Reschedule Service Checks" +msgstr "إعادة جدولة فحوصات الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:236 +msgid "Resource" +msgstr "مورد" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:89 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:124 +msgid "Resource Name" +msgstr "اسم المورد" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:165 +#, php-format +msgid "Resource configuration could not be udpated: %s. An error occured:" +msgstr "إعداد المورد لا يُمكن تحديثه: %s. حدث خطأ:" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:160 +#, php-format +msgid "Resource configuration has been successfully updated: %s" +msgstr "إعداد المورد تم تحديثه بنجاح: %s" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:85 +msgid "Restrict views to the Icinga objects that match the filter" +msgstr "تقييد العروض بكائنات إسنجا التي توافق المرشح" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:60 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:86 +msgid "Runtime Variables" +msgstr "متغيرات وقت التشغيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:77 +msgid "SCHEDULED DOWNTIME" +msgstr "زمن توقف مجدول" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:108 +msgid "SSH Identity" +msgstr "هوية SSH" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:151 +msgid "SSH port to connect to on the remote Icinga instance" +msgstr "منفذ SSH المُتصل به على حالة إسنجا البعيدة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:6 +msgctxt "Downtime status" +msgid "STARTS" +msgstr "يبدأ" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:40 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:42 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/SecurityConfigForm.php:20 +msgid "Save Changes" +msgstr "حفظ التغييرات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:179 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:181 +msgid "Schedule Check" +msgstr "فحص مجدول" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostController.php:154 +msgid "Schedule Host Downtime" +msgstr "زمن توقف مضيف مجدول" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostsController.php:199 +msgid "Schedule Host Downtimes" +msgstr "أزمنة توقف مضيف مجدولة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServiceController.php:112 +msgid "Schedule Service Downtime" +msgstr "زمن توقف خدمة مجدول" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServicesController.php:202 +msgid "Schedule Service Downtimes" +msgstr "أزمنة توقف خدمة مجدولة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:15 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:29 +msgid "" +"Schedule a downtime to suppress all problem notifications within a specific " +"period of time" +msgstr "جدولة زمن توقف لإخماد كل تنبيهات المشكلة ضمن نطاق زمني محدد" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php:34 +msgid "Schedule check" +msgid_plural "Schedule checks" +msgstr[0] "جدولة فحص" +msgstr[1] "جدولة فحوصات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php:28 +msgid "Schedule check for all services on the hosts and the hosts themselves." +msgstr "جدولة فحص لكل خدمات المضيفين والمضيفين أنفسهم." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:7 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:21 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:47 +msgid "Schedule downtime" +msgid_plural "Schedule downtimes" +msgstr[0] "زمن توقف مجدول" +msgstr[1] "أزمنة توقف مجدولة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:30 +msgid "" +"Schedule downtime for all services on the hosts and the hosts themselves." +msgstr "جدولة فحص لكل خدمات المضيفين والمضيفين أنفسهم." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:100 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:99 +msgid "Schedule downtimes" +msgstr "جدولة أزمنة توقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:49 +msgid "Schedule non-triggered downtime for all child hosts" +msgstr "جدولة زمن توقف لا يمكن تشغيله لكل المضيفين التابعين" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:42 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:56 +msgid "Schedule the next active check at a different time than the current one" +msgstr "جدول الفحص التالي النشط في زمن مختلف عن الزمن الحالي" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php:44 +msgid "Schedule the next active check to run immediately" +msgstr "جدول الفحص التالي النشط ليعمل حالا" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:48 +msgid "Schedule triggered downtime for all child hosts" +msgstr "جدولة زمن توقف يمكن تشغيله لكل المضيفين التابعين" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:72 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:100 +msgid "Scheduled" +msgstr "مجدول" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:253 +msgid "Scheduled End" +msgstr "نهاية الجدولة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:252 +msgid "Scheduled Start" +msgstr "بداية الجدولة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:127 +msgid "Scheduled end" +msgstr "نهاية الجدولة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:120 +msgid "Scheduled start" +msgstr "بداية الجدولة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php:74 +msgid "Scheduling check.." +msgid_plural "Scheduling checks.." +msgstr[0] "جدولة الفحص.." +msgstr[1] "جدولة الفحص.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php:51 +msgid "Scheduling host check.." +msgid_plural "Scheduling host checks.." +msgstr[0] "جدولة فحص المضيف.." +msgstr[1] "جدولة فحص المضيفين.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:111 +msgid "Scheduling host downtime.." +msgid_plural "Scheduling host downtimes.." +msgstr[0] "جدولة زمن توقف المضيف.." +msgstr[1] "جدولة أزمنة توقف المضيف.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php:100 +msgid "Scheduling service check.." +msgid_plural "Scheduling service checks.." +msgstr[0] "جدولة فحص خدمة.." +msgstr[1] "جدولة فحوصات خدمة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:223 +msgid "Scheduling service downtime.." +msgid_plural "Scheduling service downtimes.." +msgstr[0] "جدولة زمن توقف خدمة.." +msgstr[1] "جدولة أزمنة توقف خدمة.." + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:99 +msgid "Security" +msgstr "أمان" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostsController.php:223 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostController.php:178 +msgid "Send Custom Host Notification" +msgstr "إرسال تنبيهات مضيف مخصصة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServiceController.php:136 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServicesController.php:226 +msgid "Send Custom Service Notification" +msgstr "إرسال تنبيهات خدمة مخصصة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:123 +msgid "Send Notification" +msgstr "إرسال تنبيهات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notifications.phtml:18 +msgid "Send a custom notification to contacts responsible for this host" +msgstr "إرسال تنبيه مخصص لجهات الاتصال المسئولة عن هذا المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notifications.phtml:33 +msgid "Send a custom notification to contacts responsible for this service" +msgstr "إرسال تنبيه مخصص لجهات الاتصال المسئولة عن هذا الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:26 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contacts.phtml:44 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contacts.phtml:45 +#, php-format +msgid "Send a mail to %s" +msgstr "إرسال بريد إلى %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:29 +msgid "Send custom notification" +msgid_plural "Send custom notifications" +msgstr[0] "إرسال تنبيه مخصص" +msgstr[1] "إرسال تنبيهات مخصصة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notifications.phtml:10 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notifications.phtml:25 +msgid "Send notification" +msgstr "إرسال تنبيه" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:144 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:146 +msgid "Send notifications" +msgstr "إرسال تنبيهات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:95 +msgid "Sending custom notification.." +msgid_plural "Sending custom notifications.." +msgstr[0] "إرسال تنبيه مخصص.." +msgstr[1] "إرسال تنبيهات مخصصة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/notifications.phtml:60 +#, php-format +msgid "Sent to %s" +msgstr "إرسال إلى %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:15 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:26 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:17 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:19 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:13 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml:3 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/service-header.phtml:48 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/notifications.phtml:44 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:247 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:439 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php:199 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/DataView/DataView.php:278 +msgid "Service" +msgstr "خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:116 +msgid "Service Action" +msgstr "فعل خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:87 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:127 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:152 +msgid "Service Checks" +msgstr "فحوصات الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:156 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:574 +msgid "Service Grid" +msgstr "شبكة الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegroups.phtml:23 +msgid "Service Group" +msgstr "مجموعة خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:232 +msgid "Service Group Chart" +msgstr "مخطط مجموعة خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:487 +msgid "Service Group Name" +msgstr "اسم مجموعة خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:461 +msgid "Service Groups" +msgstr "مجموعات الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:170 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:599 +msgid "Service Name" +msgstr "اسم الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:144 +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:260 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/ok_hosts.phtml:78 +msgid "Service Problems" +msgstr "مشاكل الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:20 +msgid "Service Problems:" +msgstr "مشاكل الخدمة:" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:168 +msgid "Service Severity" +msgstr "خطورة الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hostgroups.phtml:28 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegroups.phtml:24 +msgid "Service States" +msgstr "حالات الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServiceController.php:36 +msgid "Service not found" +msgstr "الخدمة غير موجودة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:110 +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:184 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/servicegroups.phtml:17 +msgid "Servicegroups" +msgstr "مجموعات خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:108 +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:180 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:10 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:44 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:99 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:236 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServicesController.php:66 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:123 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php:212 +msgid "Services" +msgstr "الخدمات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php:53 +msgid "Set the date and time when the check should be scheduled." +msgstr "ضبط التاريخ والوقت الذي ينبغي جدولة الفحص فيه." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:89 +msgid "Set the end date and time for the downtime." +msgstr "ضبط التاريخ والوقت لزمن التوقف." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/DisableNotificationsExpireCommandForm.php:44 +msgid "Set the expire time." +msgstr "ضبط وقت انتهاء الصلاحية." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:79 +msgid "Set the start date and time for the downtime." +msgstr "ضبط تاريخ البداية والوقت لزمن التوقف." + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:114 +msgctxt "setup.summary.btn.finish" +msgid "Setup the monitoring module for Icinga Web 2" +msgstr "ضبط وحدة المراقبة لـ إسنجا وب 2" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:87 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:486 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:533 +msgid "Severity" +msgstr "الخطورة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/show-more.phtml:6 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:173 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/notifications.phtml:82 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hostgroups.phtml:270 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/comments.phtml:45 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegroups.phtml:166 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hosts.phtml:99 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/services.phtml:104 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/downtimes.phtml:48 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contacts.phtml:73 +msgid "Show More" +msgstr "عرض المزيد" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php:227 +#, php-format +msgid "Show all event records of host %s" +msgstr "عرض كل سجلات الحدث للمضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php:223 +#, php-format +msgid "Show all event records of service %s on host %s" +msgstr "عرض كل سجلات الحدث للخدمة %s على المضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TacticalController.php:20 +msgid "" +"Show an overview of all hosts and services, their current states and " +"monitoring feature utilisation" +msgstr "" +"عرض نظرة عامة عن كل الخدمات والمضيفين، وحالاتهم الحالية " +"واستخدام ميزة المراقبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:12 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/contacts.phtml:11 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contactgroups.phtml:39 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contacts.phtml:35 +#, php-format +msgid "Show detailed information about %s" +msgstr "عرض معلومات تفصيلية عن %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:137 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hosts.phtml:52 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/services.phtml:67 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/Link.php:37 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php:182 +#, php-format +msgid "Show detailed information for host %s" +msgstr "عرض معلومات تفصيلية عن المضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:151 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/services.phtml:78 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegrid.phtml:80 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/Link.php:63 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php:195 +#, php-format +msgid "Show detailed information for service %s on host %s" +msgstr "عرض معلومات تفصيلية عن الخدمة %s على المضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml:27 +#, php-format +msgid "Show detailed information for this comment about host %s" +msgstr "عرض معلومات تفصيلية لهذا التعليق عن المضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml:13 +#, php-format +msgid "Show detailed information for this comment about service %s on host %s" +msgstr "عرض معلومات تفصيلية لهذا التعليق عن الخدمة %s على المضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:35 +#, php-format +msgid "Show detailed information for this downtime scheduled for host %s" +msgstr "عرض معلومات تفصيلية عن زمن التوقف المجدول هذا للمضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:22 +#, php-format +msgid "" +"Show detailed information for this downtime scheduled for service %s on host " +"%s" +msgstr "" +"عرض معلومات تفصيلية عن زمن التوقف المجدول هذا للخدمة %s على المضيف" +"%s" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HealthController.php:30 +msgid "" +"Show information about the current monitoring instance's process and it's " +"performance as well as available features" +msgstr "" +"أظهر معلومات عن عملية حالة المراقبة الحالية " +"وأدائها فضلا عن الميزات المتوفرة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:51 +msgid "" +"Show recent alerts and visualize notifications and problems based on their " +"amount and chronological distribution" +msgstr "" +"عرض التنبيهات الأخيرة وتصور الإنذارات والمشاكل على أساس كميتها وتوزيعها الزمني" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:255 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:127 +msgid "Show resource configuration" +msgstr "عرض إعداد المورد" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HealthController.php:41 +msgid "Show statistics about the monitored objects" +msgstr "عرض إحصائيات عن الكائنات المراقبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostsController.php:61 +#, php-format +msgid "Show summarized information for %u hosts" +msgstr "عرض معلومات مختصرة عن مضيفين %u" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServicesController.php:68 +#, php-format +msgid "Show summarized information for %u services" +msgstr "عرض معلومات مختصرة عن خدمات %u" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:336 +msgid "Show the Event Grid" +msgstr "عرض شبكة الحدث" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:574 +msgid "Show the Service Grid" +msgstr "عرض شبكة الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:254 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:126 +#, php-format +msgid "Show the configuration of the %s resource" +msgstr "عرض إعداد المورد %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:24 +msgid "Show the number of historical event records grouped by time and type" +msgstr "عرض عدد سجلات الأحداث التاريخية والتي تم تجميعها حسب الوقت والنوع" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/LivestatusResourcePage.php:76 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/IdoResourcePage.php:184 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:327 +msgid "Skip Validation" +msgstr "تخطي التحقق" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:128 +msgid "Socket" +msgstr "مقبس" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/service-header.phtml:13 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/service-header.phtml:42 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/host-header.phtml:12 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hosts.phtml:38 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/services.phtml:51 +msgctxt "Soft state" +msgid "Soft" +msgstr "ناعم" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:111 +msgctxt "setup.welcome.btn.next" +msgid "Start" +msgstr "بداية" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:78 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:250 +msgid "Start Time" +msgstr "زمن البداية" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:70 +msgid "Started downtimes" +msgstr "بداية أزمنة التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:114 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:134 +msgid "State" +msgstr "حالة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/EventOverviewForm.php:44 +msgid "State Changes" +msgstr "تغييرات الحالة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HealthController.php:43 +msgid "Stats" +msgstr "إحصائيات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:53 +msgid "Status" +msgstr "الحالة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:111 +msgid "Sticky Acknowledgement" +msgstr "إشعار مثبت" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:31 +msgid "Submit Passive Check Result" +msgid_plural "Submit Passive Check Results" +msgstr[0] "تسليم نتيجة الفحص السلبي" +msgstr[1] "تسليم نتائج الفحص السلبي" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostController.php:166 +msgid "Submit Passive Host Check Result" +msgstr "تسليم نتيجة فحص المضيف السلبي" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostsController.php:211 +msgid "Submit Passive Host Check Results" +msgstr "تسليم نتائج فحص المضيف السلبي" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServiceController.php:124 +msgid "Submit Passive Service Check Result" +msgstr "تسليم نتيجة فحص الخدمة السلبي" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServicesController.php:214 +msgid "Submit Passive Service Check Results" +msgstr "تسليم نتائج فحص الخدمة السلبي" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/command.phtml:17 +#, php-format +msgid "Submit a one time or so called passive result for the %s check" +msgstr "تسليم لمرة واحدة أو ما يسمى بالنتيجة السلبية للفحص %s" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:248 +msgid "System" +msgstr "نظام" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:172 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TacticalController.php:23 +msgid "Tactical Overview" +msgstr "نظرة عامة تكتيكية" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:192 +msgid "" +"The Zend database adapter for MySQL is required to access a MySQL database." +msgstr "مطلوب محول قاعدة بيانات زند لـ MySQL للوصول إلى قاعدة بيانات MySQL." + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:212 +msgid "" +"The Zend database adapter for PostgreSQL is required to access a PostgreSQL " +"database." +msgstr "" +"مطلوب محول قاعدة بيانات زند لـ PostgreSQL للوصول إلى قاعدة بيانات PostgreSQL." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/IdoResourcePage.php:102 +msgid "The configuration has been successfully validated." +msgstr "تم التحقق من الإعداد بنجاح." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/BackendPage.php:46 +msgid "The data source used for retrieving monitoring information" +msgstr "مصدر البيانات المستخدم لاسترجاع معلومات المراقبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:143 +msgid "The date/time the scheduled downtime actually ended" +msgstr "تاريخ ووقت انتهاء زمن التوقف المجدول الفعلي" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:123 +msgid "" +"The date/time the scheduled downtime is supposed to end. If this is a " +"flexible (non-fixed) downtime, this refers to the last possible time that " +"the downtime can start" +msgstr "" +"التاريخ/الوقت الذي يفترض انتهاء زمن التوقف المجدول فيه. إذا كان " +"زمنا مرنا (غير ثابت)، فإنه يشير إلى آخر وقت من الممكن أن يبدأ " +"فيه زمن التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:116 +msgid "" +"The date/time the scheduled downtime is supposed to start. If this is a " +"flexible (non-fixed) downtime, this refers to the earliest possible time " +"that the downtime can start" +msgstr "" +"التاريخ/الوقت الذي يفترض ابتداء زمن التوقف فيه. إذا كان زمنا " +"مرنا (غير ثابت)، فإنه يشير إلى أول وقت من الممكن أن يبدأ " +"فيه زمن التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:69 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:204 +msgid "The end time must be greater than the start time" +msgstr "زمن النهاية يجب أن يكون أكبر من زمن البداية" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/DataView/DataView.php:448 +#, php-format +msgid "The filter column \"%s\" is not allowed here." +msgstr "لا يُسمح هنا بعمود المرشح \"%s\"" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:66 +msgid "The given resource name is already in use." +msgstr "اسم المورد المعطى مستخدم سابقا." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/BackendPage.php:29 +msgid "The identifier of this backend" +msgstr "رقم التعريف لهذه الخلفية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notifications.phtml:57 +#, php-format +msgid "The last one was sent %s." +msgstr "تم إرسال آخر %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:205 +msgid "" +"The name of the Icinga instance this transport should exclusively transfer " +"commands to." +msgstr "اسم حالة \"إسنجا\" التي يتحتم على هذا النقل حصريا نقل الأوامر إليها." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:38 +msgid "The name of the person who scheduled this downtime" +msgstr "اسم الشخص الذي جدول زمن التوقف هذا" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:219 +msgid "" +"The name of this command transport that is used to differentiate it from " +"others" +msgstr "اسم ناقل الأمر هذا الذي يستخدم للتفرقة بينه وبين الآخرين" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:195 +msgid "" +"The name of this monitoring backend that is used to differentiate it from " +"others" +msgstr "اسم خلفية المراقبة هذه التي تستخدم للتفرقة بينها وبين الآخرين" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:79 +msgid "" +"The performance data of this check result. Leave empty if this check result " +"has no performance data" +msgstr "" +"بيانات الأداء لنتيجة الفحص هذه. دعها فارغة إذا لم يكن لنتيجة الفحص هذه " +"بيانات أداء" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:69 +msgid "The plugin output of this check result" +msgstr "مخرج التوصيل لنتيجة الفحص هذه" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:237 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:109 +msgid "The resource to use" +msgstr "المورد المراد استخدامه" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/DataView/DataView.php:357 +#, php-format +msgid "The sort column \"%s\" is not allowed in \"%s\"." +msgstr "عمود الفرز \"%s\" لا يُسمح به في \"%s\"." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:54 +msgid "The state this check result should report" +msgstr "حالة نتيجة الفحص التي يجب أن تكون في التقرير" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:223 +msgid "The type of data source used for retrieving monitoring information" +msgstr "نوع مصدر البيانات المستخدم لاسترجاع معلومات المراقبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:381 +msgid "" +"There is currently more than one icinga instance writing to the IDO. You'll " +"see all objects from all instances without any differentation. If this is " +"not desired, consider setting up a separate IDO for each instance." +msgstr "" +"يوجد حاليا أكثر من حالة icinga تقوم بالكتابة على IDO. سترى " +"كل الكائنات من جميع الحالات دون أي اختلاف. إن لم يكن هذا " +"هو المطلوب، فانظر في إنشاء IDO منفصلة لكل حالة." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:375 +msgid "" +"There is currently no icinga instance writing to the IDO. Make sure that a " +"icinga instance is configured and able to write to the IDO." +msgstr "" +"لا يوجد حاليا أي حالة إسنجا تقوم بالكتابة على IDO. تأكد من " +"أن حالة إسنجا تم إعدادها وقادرة على الكتابة على IDO." + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php:91 +#, php-format +msgid "There is no \"%s\" monitoring backend" +msgstr "لا يوحد خلفية مراقبة \"%s\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:22 +msgid "" +"This command is used to acknowledge host or service problems. When a problem " +"is acknowledged, future notifications about problems are temporarily " +"disabled until the host or service recovers." +msgstr "" +"يستخدم هذا الأمر للإشعار بمشاكل المضيف أو الخدمة. عندما يتم الإشعار " +"بمشكلة، فإن الإخطارات المستقبلية حول المشاكل سيتم تعطيلها مؤقتا " +"حتى يتعافى المضيف أو الخدمة." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php:19 +msgid "This command is used to add host or service comments." +msgstr "يستخدم هذا الأمر لإضافة تعليقات على المضيف أو الخدمة." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/DisableNotificationsExpireCommandForm.php:26 +msgid "" +"This command is used to disable host and service notifications for a " +"specific time." +msgstr "يستخدم هذا الأمر لتعطيل التنبيهات على المضيف والخدمة لوقت محدد." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:33 +msgid "" +"This command is used to schedule host and service downtimes. During the " +"specified downtime, Icinga will not send notifications out about the hosts " +"and services. When the scheduled downtime expires, Icinga will send out " +"notifications for the hosts and services as it normally would. Scheduled " +"downtimes are preserved across program shutdowns and restarts." +msgstr "" +"يستخدم هذا الأمر لجدولة أزمنة التوقف للمضيف والخدمة. خلال فترة التوقف " +"المحددة لن يرسل إسنجا أي إخطارات عن المضيفين والخدمات. عند انتهاء" +"صلاحية زمن التوقف المجدول، ستقوم إسنجا بإرسال إخطارات للمضيفين " +"والخدمات كالمعتاد. يتم الاحتفاظ بأزمنة التوقف المجدول خلال إغلاق " +"البرنامج وإعادة تشغيله." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php:23 +msgid "" +"This command is used to schedule the next check of hosts or services. Icinga " +"will re-queue the hosts or services to be checked at the time you specify." +msgstr "" +"يستخدم هذا الأمر لجدولة الفحص التالي للمضيفين والخدمات. ستقوم إسنجا " +"بإعادة قائمة الانتظار للمضيفين والخدمات لتُفحص في الوقت الذي تحدده." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:20 +msgid "" +"This command is used to send custom notifications about hosts or services." +msgstr "يستخدم هذا الأمر لإرسال تنبيهات مخصصة حول المضيفين والخدمات." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:20 +msgid "This command is used to submit passive host or service check results." +msgstr "يستخدم هذا الأمر لتسليم نتائج فحص المضيف أو الخدمة السلبي." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:63 +msgid "This comment does not expire." +msgstr "هذا التعليق لا تنتهي صلاحيته." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml:41 +#, php-format +msgid "This comment expires on %s at %s" +msgstr "هذا التعليق تنتهي صلاحيته في %s في %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:60 +#, php-format +msgid "This comment expires on %s at %s." +msgstr "هذا التعليق تنتهي صلاحيته في %s في %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:47 +msgid "This downtime is flexible" +msgstr "زمن التوقف هذا مرن" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:51 +msgid "This downtime is in effect" +msgstr "زمن التوقف هذا ساري المفعول" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:106 +#, php-format +msgid "" +"This fixed host downtime has been scheduled to start on %s at %s and to end " +"on %s at %s." +msgstr "" +"زمن توقف المضيف الثابت هذا تمت جدولته ليبدأ في %s في %s " +"وينتهي في %s في %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:96 +#, php-format +msgid "" +"This fixed host downtime was started on %s at %s and expires on %s at %s." +msgstr "" +"زمن توقف المضيف الثابت هذا بدأ في %s في %s " +"وينتهي في %s في %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:105 +#, php-format +msgid "" +"This fixed service downtime has been scheduled to start on %s at %s and to " +"end on %s at %s." +msgstr "" +"زمن توقف الخدمة الثابت هذه تمت جدولته ليبدأ في %s في %s " +"وينتهي في %s في %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:95 +#, php-format +msgid "" +"This fixed service downtime was started on %s at %s and expires on %s at %s." +msgstr "زمن توقف الخدمة الثابت هذا بدأ في %s في %s وينتهي في %s في %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:85 +#, php-format +msgid "" +"This flexible host downtime has been scheduled to start between %s - %s and " +"to last for %s." +msgstr "" +"زمن توقف المضيف المرن هذا تمت جدولته ليبدأ بين %s - %s " +"ويستمر إلى %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:74 +#, php-format +msgid "" +"This flexible host downtime was started on %s at %s and lasts for %s until " +"%s at %s." +msgstr "" +"زمن توقف المضيف المرن هذا بدأ في s في %s ويستمر حتى %s " +"إلى %s في %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:84 +#, php-format +msgid "" +"This flexible service downtime has been scheduled to start between %s - %s " +"and to last for %s." +msgstr "" +"زمن توقف الخدمة المرن هذا تمت جدولته ليبدأ بين %s - %s " +"ويستمر إلى %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:73 +#, php-format +msgid "" +"This flexible service downtime was started on %s at %s and lasts for %s " +"until %s at %s." +msgstr "" +"زمن توقف الخدمة المرن هذا بدأ في s في %s ويستمر حتى %s " +"إلى %s في %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/WelcomePage.php:33 +msgid "This is the core module for Icinga Web 2." +msgstr "هذه هي الوحدة الأساسية في إسنجا وب 2." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:54 +msgid "This notification was not sent out to any contact." +msgstr "لم يتم إرسال هذا التنبيه لأي جهة اتصال." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:22 +msgid "Time to Reaction (Ack, Recover)" +msgstr "وقت رد الفعل (إشعار، استرداد)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:109 +msgid "TimeLine interval" +msgstr "فترة الجدول الزمني" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:228 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:25 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:29 +msgid "Timeline" +msgstr "الجدول الزمني" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:82 +msgid "To" +msgstr "إلى" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:183 +msgid "" +"To access the IDO stored in a MySQL database the PDO-MySQL module for PHP is " +"required." +msgstr "" +"للوصول إلى الـ IDO المخزنة في قاعدة بيانات \"ماي سيكول\" فإن وحدة PDO-MySQL" +" للـ PHP " +"مطلوبة." + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:203 +msgid "" +"To access the IDO stored in a PostgreSQL database the PDO-PostgreSQL module " +"for PHP is required." +msgstr "" +"للوصول إلى الـ IDO المخزنة في قاعدة بيانات PostgreSQL فإن وحدة PDO-PostgreSQL " +"للـ PHP مطلوبة." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/SecurityPage.php:16 +msgid "" +"To protect your monitoring environment against prying eyes please fill out " +"the settings below." +msgstr "" +"لحماية بيئة المراقبة الخاصة بك ضد أعين المتطفلين الرجاء تعبئة " +"الإعدادات أدناه." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:85 +msgid "Today" +msgstr "اليوم" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:56 +msgid "Top 5 Recent Alerts" +msgstr "آخر أعلى 5 إنذارات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:68 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:94 +msgid "Total" +msgstr "الكلي" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:535 +msgid "Total Hosts" +msgstr "إجمالي المضيفين" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:488 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:536 +msgid "Total Services" +msgstr "إجمالي الخدمات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:76 +msgid "Transport" +msgstr "النقل" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:217 +msgid "Transport Name" +msgstr "اسم النقل" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:241 +msgid "Transport Type" +msgstr "نوع النقل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:29 +msgid "Trend" +msgstr "اتجاه" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:39 +msgid "Trend for the last 24h" +msgstr "الاتجاه في آخر 24 ساعة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:99 +msgid "Type" +msgstr "نوع" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:39 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:94 +#, php-format +msgid "Type: %s" +msgstr "نوع: %s" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/Service.php:199 +msgid "UNKNOWN" +msgstr "مجهول" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:59 +msgctxt "icinga.state" +msgid "UNKNOWN" +msgstr "مجهول" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/Host.php:188 +msgid "UNREACHABLE" +msgstr "لا يمكن الوصول إليه" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:128 +msgctxt "icinga.state" +msgid "UNREACHABLE" +msgstr "لا يمكن الوصول للوجهة" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/Host.php:182 +msgid "UP" +msgstr "قيد التشغيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:123 +msgctxt "icinga.state" +msgid "UP" +msgstr "قيد التشغيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/service/statusicons.phtml:5 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/host/statusicons.phtml:5 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/HostFlags.php:13 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/ServiceFlags.php:9 +msgid "Unhandled" +msgstr "غير مُعالج" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:14 +msgid "Unhandled Problems:" +msgstr "مشاكل غير معالجة:" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:119 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:264 +msgid "Unknown" +msgstr "مجهول" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:139 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:314 +msgid "Unreachable" +msgstr "لا يمكن الوصول إليه" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:137 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:300 +msgid "Up" +msgstr "قيد التشغيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:72 +msgid "Use Expire Time" +msgstr "استخدام وقت انتهاء الصلاحية" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:88 +msgid "Use SSH Identity" +msgstr "استخدام هوية SSH" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:40 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:43 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:163 +msgid "User" +msgstr "مستخدم" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml:10 +msgid "User Comment" +msgstr "تعليق مستخدم" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:165 +msgid "" +"User to log in as on the remote Icinga instance. Please note that key-based " +"SSH login must be possible for this user" +msgstr "" +"المستخدم الذي يمكن الدخول بواسطته لحالة إسنجا البعيدة. يرجى ملاحظة أن " +"دخول SSH المبني على المفاتيح يجب أن يكون متاحا لهذا المستخدم" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:109 +msgid "Username" +msgstr "اسم المستخدم" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:124 +msgid "Validate Configuration" +msgstr "التحقق من الإعداد" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:125 +msgid "Validation In Progress" +msgstr "جار التحقق" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/IdoResourcePage.php:131 +msgid "Validation Log" +msgstr "سجل التحقق" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/Perfdata.php:37 +msgid "Value" +msgstr "قيمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:623 +msgid "Value for interval not valid" +msgstr "قيمة الفترة ليست صالحة" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/Service.php:193 +msgid "WARNING" +msgstr "تحذير" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:57 +msgctxt "icinga.state" +msgid "WARNING" +msgstr "تحذير" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/Perfdata.php:40 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:118 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:250 +msgid "Warning" +msgstr "تحذير" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/WelcomePage.php:21 +msgid "Welcome to the configuration of the monitoring module for Icinga Web 2!" +msgstr "مرحبا بك في إعداد وحدة المراقبة لـ إسنجا وب 2!" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:155 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:48 +msgid "Yes" +msgstr "نعم" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:189 +msgid "Zend database adapter for MySQL" +msgstr "محول زند لقاعدة بيانات ماي سيكول" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:209 +msgid "Zend database adapter for PostgreSQL" +msgstr "محول زند لقاعدة بيانات بوستجرى إس كيو إل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml:22 +msgid "acknowledged" +msgstr "معترف به" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/timeline/index.phtml:60 +#, php-format +msgctxt "timeline.link.title.datetime.twice" +msgid "between %s and %s" +msgstr "بين %s و%s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:42 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml:34 +msgid "by" +msgstr "بواسطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:80 +msgid "changed" +msgstr "غُيِّر" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/comments.phtml:51 +msgid "commented" +msgstr "عُلِّق" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:79 +msgid "created" +msgstr "أُنشيء" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:84 +msgid "disable" +msgstr "تعطيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:194 +msgid "down" +msgstr "متوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:85 +msgid "enable" +msgstr "تفعيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:60 +#, php-format +msgctxt "Last format parameter represents the end time" +msgid "ends %s" +msgstr "انتهاء %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:28 +#, php-format +msgid "expected %s" +msgstr "متوقع %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:54 +#, php-format +msgctxt "Last format parameter represents the downtime expire time" +msgid "expires %s" +msgstr "تنتهي صلاحيته %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:69 +msgid "hard state" +msgstr "حالة صعبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:138 +msgid "he date/time the scheduled downtime was actually started" +msgstr "التاريخ والوقت الذي ابتدأ فيه فعليا زمن التوقف المجدول" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:29 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/MonitoredObject.php:735 +msgid "host" +msgstr "مضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/timeline/index.phtml:48 +#, php-format +msgctxt "timeline.link.title.month.and.year" +msgid "in %s" +msgstr "في %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/timeline/index.phtml:54 +#, php-format +msgctxt "timeline.link.title.year" +msgid "in %s" +msgstr "في %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:37 +msgid "in the last hour" +msgstr "في الساعة الأخيرة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/timeline/index.phtml:41 +#, php-format +msgctxt "timeline.link.title.week.and.year" +msgid "in week %s of %s" +msgstr "في الاسبوع %s من %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checksource.phtml:9 +msgid "is not reachable" +msgstr "لا يمكن الوصول إليه" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checksource.phtml:9 +msgid "is reachable" +msgstr "يمكن الوصول إليه" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:35 +msgid "notifications per hour" +msgstr "التنبيهات في الساعة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/timeline/index.phtml:35 +#, php-format +msgctxt "timeline.link.title.time" +msgid "on %s" +msgstr "في %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:65 +#, php-format +msgctxt "Last format parameter represents the time scheduled" +msgid "scheduled %s" +msgstr "مجدول في %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:70 +#, php-format +msgctxt "Last format parameter represents the time scheduled" +msgid "scheduled flexible %s" +msgstr "مجدول بصورة مرنة في %s" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/MonitoredObject.php:738 +msgid "service" +msgstr "خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:69 +msgid "soft state" +msgstr "حالة ناعمة" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:53 +msgctxt "setup.summary.subject" +msgid "the monitoring module" +msgstr "وحدة المراقبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:192 +msgid "unchanged" +msgstr "دون تغيير" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:196 +msgid "up" +msgstr "قيد التشغيل" + + diff --git a/modules/monitoring/application/locale/de_DE/LC_MESSAGES/monitoring.mo b/modules/monitoring/application/locale/de_DE/LC_MESSAGES/monitoring.mo index 3560dc336..8a508762f 100644 Binary files a/modules/monitoring/application/locale/de_DE/LC_MESSAGES/monitoring.mo and b/modules/monitoring/application/locale/de_DE/LC_MESSAGES/monitoring.mo differ diff --git a/modules/monitoring/application/locale/de_DE/LC_MESSAGES/monitoring.po b/modules/monitoring/application/locale/de_DE/LC_MESSAGES/monitoring.po index 79a04cf40..adfad0abe 100644 --- a/modules/monitoring/application/locale/de_DE/LC_MESSAGES/monitoring.po +++ b/modules/monitoring/application/locale/de_DE/LC_MESSAGES/monitoring.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: Monitoring Module (2.1.2)\n" "Report-Msgid-Bugs-To: dev@icinga.org\n" "POT-Creation-Date: 2016-02-29 14:40+0000\n" -"PO-Revision-Date: 2016-04-26 12:08+0200\n" +"PO-Revision-Date: 2016-12-07 17:02+0100\n" "Last-Translator: Thomas Gelf \n" "Language: de_DE\n" "MIME-Version: 1.0\n" @@ -16,7 +16,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "Language-Team: \n" -"X-Generator: Poedit 1.8.7.1\n" +"X-Generator: Poedit 1.8.11\n" #: /vagrant/modules/monitoring/application/controllers/ChartController.php:375 msgid " Down Hosts (Handled)" @@ -1816,7 +1816,16 @@ msgstr "" "Backend mit dem Namen “%s” und die weiter unten spezifizierte Ressourcen " "abrufen:" -#: /vagrant/modules/monitoring/library/Monitoring/TransportStep.php:76 +#: /vagrant/modules/monitoring/library/Monitoring/TransportStep.php:87 +msgid "" +"Icinga Web 2 will use the Icinga 2 API to send commands to your monitoring " +"instance by using the connection details listed below:" +msgstr "" +"Icinga Web 2 wird die Icinga 2 API verwenden, um Kommandos an Ihre " +"Monitoringinstanz zu senden. Dazu werden die folgenden " +"Verbindungseinstellungen verwendet:" + +#: /vagrant/modules/monitoring/library/Monitoring/TransportStep.php:49 #, php-format msgid "" "Icinga Web 2 will use the named pipe located at \"%s\" to send commands to " @@ -1919,8 +1928,8 @@ msgstr "" #: /vagrant/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:45 msgid "" "If you work with other administrators, you may find it useful to share " -"information about the the host or service that is having problems. Make sure " -"you enter a brief description of what you are doing." +"information about the host or service that is having problems. Make sure you " +"enter a brief description of what you are doing." msgstr "" "Wenn Sie mit anderen Administratoren zusammenarbeiten, werden Sie es " "nützlich finden, Informationen zu Hosts oder Services mit Problemen " diff --git a/modules/monitoring/application/locale/it_IT/LC_MESSAGES/monitoring.po b/modules/monitoring/application/locale/it_IT/LC_MESSAGES/monitoring.po index ec0f3e5f6..8f752efba 100644 --- a/modules/monitoring/application/locale/it_IT/LC_MESSAGES/monitoring.po +++ b/modules/monitoring/application/locale/it_IT/LC_MESSAGES/monitoring.po @@ -1872,7 +1872,7 @@ msgstr "" #: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php:45 msgid "" "If you work with other administrators, you may find it useful to share " -"information about the the host or service that is having problems. Make sure " +"information about the host or service that is having problems. Make sure " "you enter a brief description of what you are doing." msgstr "" "Se lavori con altri amministratori potresti trovare utile condividere le " diff --git a/modules/monitoring/application/locale/pt_BR/LC_MESSAGES/monitoring.po b/modules/monitoring/application/locale/pt_BR/LC_MESSAGES/monitoring.po index 9edbc328c..2f6b97aa4 100644 --- a/modules/monitoring/application/locale/pt_BR/LC_MESSAGES/monitoring.po +++ b/modules/monitoring/application/locale/pt_BR/LC_MESSAGES/monitoring.po @@ -1553,7 +1553,7 @@ msgstr "" #: /usr/local/icingaweb/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:73 msgid "" "If you work with other administrators, you may find it useful to share " -"information about the the host or service that is having problems. Make sure " +"information about the host or service that is having problems. Make sure " "you enter a brief description of what you are doing." msgstr "" "Se você trabalha com outros administradores, você pode achar isso útil para " diff --git a/modules/monitoring/application/locale/ru_RU/LC_MESSAGES/monitoring.po b/modules/monitoring/application/locale/ru_RU/LC_MESSAGES/monitoring.po index bcaf5fb6d..655f0adce 100644 --- a/modules/monitoring/application/locale/ru_RU/LC_MESSAGES/monitoring.po +++ b/modules/monitoring/application/locale/ru_RU/LC_MESSAGES/monitoring.po @@ -1819,7 +1819,7 @@ msgstr "" #: /vagrant/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:45 msgid "" "If you work with other administrators, you may find it useful to share " -"information about the the host or service that is having problems. Make sure " +"information about the host or service that is having problems. Make sure " "you enter a brief description of what you are doing." msgstr "" diff --git a/modules/monitoring/application/views/helpers/HostFlags.php b/modules/monitoring/application/views/helpers/HostFlags.php index a4c5bc7d2..81d8ebcdf 100644 --- a/modules/monitoring/application/views/helpers/HostFlags.php +++ b/modules/monitoring/application/views/helpers/HostFlags.php @@ -1,9 +1,6 @@ view->icon('eye-off', $this->view->translate('Active Checks Disabled')); } } - return $icons; + return implode(' ', $icons); } } diff --git a/modules/monitoring/application/views/helpers/Perfdata.php b/modules/monitoring/application/views/helpers/Perfdata.php index 06443b8f1..6b9113830 100644 --- a/modules/monitoring/application/views/helpers/Perfdata.php +++ b/modules/monitoring/application/views/helpers/Perfdata.php @@ -81,7 +81,7 @@ class Zend_View_Helper_Perfdata extends Zend_View_Helper_Abstract $data []= sprintf( '%s', $text, - StringHelper::ellipsisCenter($text, 24) + $text ); } } diff --git a/modules/monitoring/application/views/helpers/PluginOutput.php b/modules/monitoring/application/views/helpers/PluginOutput.php index ba3013a3a..92500e4e2 100644 --- a/modules/monitoring/application/views/helpers/PluginOutput.php +++ b/modules/monitoring/application/views/helpers/PluginOutput.php @@ -1,10 +1,25 @@ fixLinks($output); - // Help browsers to break words in plugin output $output = trim($output); - // Add space after comma where missing - $output = preg_replace('/,[^\s]/', ', ', $output); - // Add zero width space after ')', ']', ':', '.', '_' and '-' if not surrounded by whitespaces - $output = preg_replace('/([^\s])([\\)\\]:._-])([^\s])/', '$1$2​$3', $output); - // Add zero width space before '(' and '[' if not surrounded by whitespaces - $output = preg_replace('/([^\s])([([])([^\s])/', '$1​$2$3', $output); - + // Add space after comma where missing, to help browsers to break words in plugin output + $output = preg_replace('/,(?=[^\s])/', ', ', $output); if (! $raw) { if ($isHtml) { + $output = $this->processHtml($output); $output = '
' . $output . '
'; } else { $output = '
' . $output . '
'; @@ -70,32 +92,68 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract return $output; } - protected function fixLinks($html) + /** + * Replace classic Icinga CGI links with Icinga Web 2 links and color state information, if any + * + * @param string $html + * + * @return string + */ + protected function processHtml($html) { - - $ret = array(); - $dom = new DOMDocument; - $dom->loadXML('
' . $html . '
', LIBXML_NOERROR | LIBXML_NOWARNING); - $dom->preserveWhiteSpace = false; - $links = $dom->getElementsByTagName('a'); - foreach ($links as $tag) - { - $href = $tag->getAttribute('href'); - if (preg_match('~^/cgi\-bin/status\.cgi\?(.+)$~', $href, $m)) { - parse_str($m[1], $params); - if (isset($params['host'])) { - $tag->setAttribute('href', $this->view->baseUrl( - '/monitoring/host/show?host=' . urlencode($params['host'] - ))); + $pattern = '/[([](OK|WARNING|CRITICAL|UNKNOWN)[)\]]/'; + $doc = new DOMDocument(); + $doc->loadXML('
' . $html . '
', LIBXML_NOERROR | LIBXML_NOWARNING); + $dom = new RecursiveIteratorIterator(new DomNodeIterator($doc), RecursiveIteratorIterator::SELF_FIRST); + $nodesToRemove = array(); + foreach ($dom as $node) { + /** @var \DOMNode $node */ + if ($node->nodeType === XML_TEXT_NODE) { + $start = 0; + while (preg_match($pattern, $node->nodeValue, $match, PREG_OFFSET_CAPTURE, $start)) { + $offsetLeft = $match[0][1]; + $matchLength = strlen($match[0][0]); + $leftLength = $offsetLeft - $start; + if ($leftLength) { + $text = new DOMText(substr($node->nodeValue, $start, $leftLength)); + $node->parentNode->insertBefore($text, $node); + } + $span = $doc->createElement('span', $match[0][0]); + $span->setAttribute('class', 'state-' . strtolower($match[1][0])); + $node->parentNode->insertBefore($span, $node); + $start = $offsetLeft + $matchLength; + } + if ($start) { + $nodesToRemove[] = $node; + } + } elseif ($node->nodeType === XML_ELEMENT_NODE) { + /** @var \DOMElement $node */ + if ($node->tagName === 'a' + && preg_match('~^/cgi\-bin/status\.cgi\?(.+)$~', $node->getAttribute('href'), $match) + ) { + parse_str($match[1], $params); + if (isset($params['host'])) { + $node->setAttribute( + 'href', + $this->view->baseUrl('/monitoring/host/show?host=' . urlencode($params['host'])) + ); + } } - } else { - // ignoring } - //$ret[$tag->getAttribute('href')] = $tag->childNodes->item(0)->nodeValue; } - return substr($dom->saveHTML(), 5, -7); + foreach ($nodesToRemove as $node) { + /** @var \DOMNode $node */ + $node->parentNode->removeChild($node); + } + + return substr($doc->saveHTML(), 5, -7); } + /** + * Initialize and return self::$purifier + * + * @return HTMLPurifier + */ protected function getPurifier() { if (self::$purifier === null) { @@ -105,7 +163,8 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract $config = HTMLPurifier_Config::createDefault(); $config->set('Core.EscapeNonASCIICharacters', true); - $config->set('HTML.Allowed', 'p,br,b,a[href],i,table,tr,td[colspan],div,*[class]'); + $config->set('HTML.Allowed', 'p,br,b,a[href|target],i,table,tr,th[colspan],td[colspan],div,*[class]'); + $config->set('Attr.AllowedFrameTargets', array('_blank')); // This avoids permission problems: // $config->set('Core.DefinitionCache', null); $config->set('Cache.DefinitionImpl', null); diff --git a/modules/monitoring/application/views/helpers/ServiceFlags.php b/modules/monitoring/application/views/helpers/ServiceFlags.php index e9bfd70ba..47a351c4c 100644 --- a/modules/monitoring/application/views/helpers/ServiceFlags.php +++ b/modules/monitoring/application/views/helpers/ServiceFlags.php @@ -3,30 +3,31 @@ class Zend_View_Helper_ServiceFlags extends Zend_View_Helper_Abstract { - public function serviceFlags($service) { + public function serviceFlags($service) + { $icons = array(); - if (!$service->service_handled && $service->service_state > 0) { + if (! $service->service_handled && $service->service_state > 0) { $icons[] = $this->view->icon('attention-alt', $this->view->translate('Unhandled')); } - if ($service->service_acknowledged && !$service->service_in_downtime) { + if ($service->service_acknowledged) { $icons[] = $this->view->icon('ok', $this->view->translate('Acknowledged')); } if ($service->service_is_flapping) { $icons[] = $this->view->icon('flapping', $this->view->translate('Flapping')); } - if (!$service->service_notifications_enabled) { + if (! $service->service_notifications_enabled) { $icons[] = $this->view->icon('bell-off-empty', $this->view->translate('Notifications Disabled')); } if ($service->service_in_downtime) { $icons[] = $this->view->icon('plug', $this->view->translate('In Downtime')); } - if (!$service->service_active_checks_enabled) { - if (!$service->service_passive_checks_enabled) { + if (! $service->service_active_checks_enabled) { + if (! $service->service_passive_checks_enabled) { $icons[] = $this->view->icon('eye-off', $this->view->translate('Active And Passive Checks Disabled')); } else { - $icons[] = $this->view->icon('eye-off', $this->view->translate('Active Checks Disabled')); + $icons[] = $this->view->icon('eye-off', $this->view->translate('Active Checks Disabled')); } } - return $icons; + return implode(' ', $icons); } -} \ No newline at end of file +} diff --git a/modules/monitoring/application/views/scripts/chart/hostgroup.phtml b/modules/monitoring/application/views/scripts/chart/hostgroup.phtml deleted file mode 100644 index 22a8a8c05..000000000 --- a/modules/monitoring/application/views/scripts/chart/hostgroup.phtml +++ /dev/null @@ -1,9 +0,0 @@ - -compact) { ?> -
- render(); ?> - render() : ''; ?> -
- - render(); ?> - diff --git a/modules/monitoring/application/views/scripts/chart/servicegroup.phtml b/modules/monitoring/application/views/scripts/chart/servicegroup.phtml deleted file mode 100644 index f24efe5ba..000000000 --- a/modules/monitoring/application/views/scripts/chart/servicegroup.phtml +++ /dev/null @@ -1,8 +0,0 @@ - -compact) { ?> -
- render(); ?> -
- - render(); ?> - diff --git a/modules/monitoring/application/views/scripts/chart/test.phtml b/modules/monitoring/application/views/scripts/chart/test.phtml deleted file mode 100644 index 214f44d33..000000000 --- a/modules/monitoring/application/views/scripts/chart/test.phtml +++ /dev/null @@ -1,5 +0,0 @@ - -
- render() ?> -
- \ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/config/index.phtml b/modules/monitoring/application/views/scripts/config/index.phtml index 091aaf70f..79a8ccd3c 100644 --- a/modules/monitoring/application/views/scripts/config/index.phtml +++ b/modules/monitoring/application/views/scripts/config/index.phtml @@ -92,7 +92,7 @@ ); ?> (translate('Type: %s'), - $config->host !== null ? $this->translate('Remote') : $this->translate('Local') + ucfirst($config->get('transport', 'local')) ) ?>) diff --git a/modules/monitoring/application/views/scripts/host/show.phtml b/modules/monitoring/application/views/scripts/host/show.phtml index badf7ffcf..72f5af4a8 100644 --- a/modules/monitoring/application/views/scripts/host/show.phtml +++ b/modules/monitoring/application/views/scripts/host/show.phtml @@ -1,15 +1,14 @@ - -
+
compact): ?> tabs ?> render('partials/object/host-header.phtml') ?> - stats = $object->stats; $this->baseFilter = Filter::where('host', $object->host_name); echo $this->render('list/components/servicesummary.phtml'); - ?> +?> + render('partials/object/quick-actions.phtml') ?>
- render('partials/object/detail-content.phtml') ?> diff --git a/modules/monitoring/application/views/scripts/hosts/show.phtml b/modules/monitoring/application/views/scripts/hosts/show.phtml index 683680663..48c4b5254 100644 --- a/modules/monitoring/application/views/scripts/hosts/show.phtml +++ b/modules/monitoring/application/views/scripts/hosts/show.phtml @@ -31,8 +31,8 @@ $acknowledgeLink, null, array( - 'icon' => 'ok', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'check' ) ) ?> @@ -63,8 +63,8 @@ $addCommentLink, null, array( - 'icon' => 'comment', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'comment-empty' ) ) ?> @@ -145,8 +145,8 @@ $sendCustomNotificationLink, null, array( - 'icon' => 'bell-alt', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'bell' ) ) ?> @@ -167,8 +167,8 @@ $processCheckResultAllLink, null, array( - 'icon' => 'reply', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'edit' ) ) ?> @@ -189,8 +189,8 @@ $rescheduleAllLink, null, array( - 'icon' => 'reschedule', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'calendar-empty' ) ) ?> diff --git a/modules/monitoring/application/views/scripts/list/hosts.phtml b/modules/monitoring/application/views/scripts/list/hosts.phtml index 6de773435..da00fccc6 100644 --- a/modules/monitoring/application/views/scripts/list/hosts.phtml +++ b/modules/monitoring/application/views/scripts/list/hosts.phtml @@ -4,8 +4,6 @@ use Icinga\Module\Monitoring\Object\Host; if (! $this->compact): ?>
tabs ?> - render('list/components/hostssummary.phtml') ?> - render('list/components/selectioninfo.phtml') ?> paginator ?>
limiter ?> @@ -82,7 +80,7 @@ if (! $this->compact): ?> ) ) ?>) - hostFlags($host)) ?> + hostFlags($host) ?>

pluginOutput($this->ellipsis($host->host_output, 10000), true) ?>

@@ -107,3 +105,10 @@ if (! $this->compact): ?>
+compact): ?> +
+
+ render('list/components/hostssummary.phtml') ?> + render('list/components/selectioninfo.phtml') ?> +
+ diff --git a/modules/monitoring/application/views/scripts/list/notifications.phtml b/modules/monitoring/application/views/scripts/list/notifications.phtml index 8316665d1..dc34a8c88 100644 --- a/modules/monitoring/application/views/scripts/list/notifications.phtml +++ b/modules/monitoring/application/views/scripts/list/notifications.phtml @@ -35,7 +35,7 @@ if (! $this->compact): ?>
- timeAgo($notification->notification_start_time, $this->compact) ?> + timeAgo($notification->notification_timestamp, $this->compact) ?>
diff --git a/modules/monitoring/application/views/scripts/list/servicegrid.phtml b/modules/monitoring/application/views/scripts/list/servicegrid.phtml index 2d50a067d..845cac745 100644 --- a/modules/monitoring/application/views/scripts/list/servicegrid.phtml +++ b/modules/monitoring/application/views/scripts/list/servicegrid.phtml @@ -81,7 +81,7 @@ $hostFilter = '(host_name=' . implode('|host_name=', array_keys($pivotData)) . ' $service->service_display_name, $service->host_display_name ), - 'class' => 'bg-color-' . Service::getStateText($service->service_state) . ($service->service_handled ? ' handled' : ''), + 'class' => 'service-grid-link state-' . Service::getStateText($service->service_state) . ($service->service_handled ? ' handled' : ''), 'title' => $service->service_output ) ) ?> diff --git a/modules/monitoring/application/views/scripts/list/services.phtml b/modules/monitoring/application/views/scripts/list/services.phtml index d8fd5d4b5..c9f1e855b 100644 --- a/modules/monitoring/application/views/scripts/list/services.phtml +++ b/modules/monitoring/application/views/scripts/list/services.phtml @@ -5,8 +5,6 @@ use Icinga\Module\Monitoring\Object\Service; if (! $this->compact): ?>
tabs ?> - render('list/components/servicesummary.phtml') ?> - render('list/components/selectioninfo.phtml') ?> paginator ?>
limiter ?> @@ -82,7 +80,7 @@ if (! $this->compact): ?> 'class' => 'rowaction' ) ) ?> - serviceFlags($service)) ?> + serviceFlags($service) ?>
@@ -112,3 +110,10 @@ if (! $this->compact): ?>
+compact): ?> +
+
+ render('list/components/servicesummary.phtml') ?> + render('list/components/selectioninfo.phtml') ?> +
+ diff --git a/modules/monitoring/application/views/scripts/partials/command/object-command-form.phtml b/modules/monitoring/application/views/scripts/partials/command/object-command-form.phtml index 34e31f897..1359e5f36 100644 --- a/modules/monitoring/application/views/scripts/partials/command/object-command-form.phtml +++ b/modules/monitoring/application/views/scripts/partials/command/object-command-form.phtml @@ -1,21 +1,17 @@ -
- compact): ?> - tabs; ?> - - getType() === $object::TYPE_HOST): ?> - render('partials/object/host-header.phtml'); ?> - compact): ?> + tabs ?> + +getType() === $object::TYPE_HOST) { + echo $this->render('partials/object/host-header.phtml'); $this->baseFilter = Filter::where('host', $object->host_name); $this->stats = $object->stats; echo $this->render('list/components/servicesummary.phtml'); - ?> - - render('partials/object/service-header.phtml'); ?> -
- +} else { + echo $this->render('partials/object/service-header.phtml'); +} ?>
- +
diff --git a/modules/monitoring/application/views/scripts/partials/command/objects-command-form.phtml b/modules/monitoring/application/views/scripts/partials/command/objects-command-form.phtml index dbb8829fe..8d241eed0 100644 --- a/modules/monitoring/application/views/scripts/partials/command/objects-command-form.phtml +++ b/modules/monitoring/application/views/scripts/partials/command/objects-command-form.phtml @@ -1,19 +1,15 @@
- - compact): ?> - - - - - render('list/components/servicesummary.phtml'); ?> - render('partials/service/objects-header.phtml'); ?> - +compact): ?> + + + + render('list/components/servicesummary.phtml') ?> + render('partials/service/objects-header.phtml') ?> + render('list/components/hostssummary.phtml') ?> - render('partials/host/objects-header.phtml'); ?> - + render('partials/host/objects-header.phtml') ?> +
-
-
- +
diff --git a/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml b/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml index 6d27a14a4..2ed932916 100644 --- a/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml +++ b/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml @@ -43,19 +43,28 @@ $this->formatTime($comment->expiration) )) : '' ?> setAttrib('class', $deleteButton->getAttrib('class') . ' remove-action'); $deleteButton->populate( array( 'comment_id' => $comment->id, - 'comment_is_service' => isset($comment->service_description) + 'comment_is_service' => isset($comment->service_description), + 'comment_name' => $comment->name ) ); + $deleteButton->getElement('btn_submit') + ->setAttrib('aria-label', $this->translate('Delete comment')) + ->setAttrib('id', $buttonId) + ->setAttrib('aria-describedby', $buttonId . ' ' . $textId); echo $deleteButton; } ?>
-

+

id=""> nl2br($this->escapeComment($comment->comment)) ?>

diff --git a/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml b/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml index 96130db7a..bfe88a761 100644 --- a/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml +++ b/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml @@ -53,20 +53,28 @@ setAttrib('class', $deleteButton->getAttrib('class') . ' remove-action'); $deleteButton->populate( array( - 'downtime_id' => $downtime->id, - 'downtime_is_service' => isset($downtime->service_description) + 'downtime_id' => $downtime->id, + 'downtime_is_service' => isset($downtime->service_description), + 'downtime_name' => $downtime->name ) ); + $deleteButton->getElement('btn_submit') + ->setAttrib('aria-label', $this->translate('Delete downtime')) + ->setAttrib('id', $buttonId) + ->setAttrib('aria-describedby', $buttonId . ' ' . $textId); echo $deleteButton; } ?> -

+

id=""> nl2br($this->escapeComment($downtime->comment)) ?>

diff --git a/modules/monitoring/application/views/scripts/partials/event-history.phtml b/modules/monitoring/application/views/scripts/partials/event-history.phtml index 67236bd6a..0d9c64870 100644 --- a/modules/monitoring/application/views/scripts/partials/event-history.phtml +++ b/modules/monitoring/application/views/scripts/partials/event-history.phtml @@ -40,12 +40,14 @@ $lastDate = null; peekAhead() as $event): $icon = ''; $iconCssClass = ''; + $iconTitle = null; $isService = isset($event->service_description); $msg = $event->output; $stateName = 'no-state'; switch ($event->type) { case 'notify': - $icon = 'bell-alt'; + $icon = 'bell'; + $iconTitle = $this->translate('Notification', 'tooltip'); $label = $this->translate('NOTIFICATION'); $msg = $msg ? preg_replace_callback( '/^\[([^\]]+)\]/', @@ -55,56 +57,73 @@ $lastDate = null; $stateName = $isService ? Service::getStateText($event->state) : Host::getStateText($event->state); break; case 'comment': - $icon = 'comment'; + $icon = 'comment-empty'; + $iconTitle = $this->translate('Comment', 'tooltip'); $label = $this->translate('COMMENT'); break; case 'comment_deleted': $icon = 'cancel'; + $iconTitle = $this->translate('Comment removed', 'tooltip'); $label = $this->translate('COMMENT DELETED'); break; case 'ack': $icon = 'ok'; + $iconTitle = $this->translate('Acknowledged', 'tooltip'); $label = $this->translate('ACKNOWLEDGED'); break; case 'ack_deleted': $icon = 'ok'; $iconCssClass = 'icon-strikethrough'; + $iconTitle = $this->translate('Acknowledgement removed', 'tooltip'); $label = $this->translate('ACKNOWLEDGEMENT REMOVED'); break; case 'dt_comment': - // TODO(el): Does not appear in history $icon = 'plug'; + $iconTitle = $this->translate('Downtime scheduled', 'tooltip'); $label = $this->translate('SCHEDULED DOWNTIME'); break; case 'dt_comment_deleted': - // TODO(el): Does not appear in history $icon = 'plug'; $iconCssClass = 'icon-strikethrough'; + $iconTitle = $this->translate('Downtime removed', 'tooltip'); $label = $this->translate('DOWNTIME DELETED'); break; case 'flapping': - // TODO(el): Icon + $icon = 'flapping'; + $iconTitle = $this->translate('Flapping started', 'tooltip'); $label = $this->translate('FLAPPING'); break; case 'flapping_deleted': - // TODO(el): Icon + $icon = 'flapping'; + $iconCssClass = 'icon-strikethrough'; + $iconTitle = $this->translate('Flapping stopped', 'tooltip'); $label = $this->translate('FLAPPING STOPPED'); break; case 'hard_state': + if ((int) $event->state === 0) { + $icon = 'thumbs-up'; + } else { + $icon = 'warning-empty'; + } + $iconTitle = $this->translate('Hard state', 'tooltip'); $label = $isService ? Service::getStateText($event->state, true) : Host::getStateText($event->state, true); $stateName = $isService ? Service::getStateText($event->state) : Host::getStateText($event->state); break; case 'soft_state': + $icon = 'spinner'; + $iconTitle = $this->translate('Soft state', 'tooltip'); $label = $isService ? Service::getStateText($event->state, true) : Host::getStateText($event->state, true); $stateName = $isService ? Service::getStateText($event->state) : Host::getStateText($event->state); break; case 'dt_start': $icon = 'plug'; + $iconTitle = $this->translate('Downtime started', 'tooltip'); $label = $this->translate('DOWNTIME START'); break; case 'dt_end': $icon = 'plug'; $iconCssClass = 'icon-strikethrough'; + $iconTitle = $this->translate('Downtime ended', 'tooltip'); $label = $this->translate('DOWNTIME END'); break; } ?> @@ -157,7 +176,7 @@ $lastDate = null;

icon($icon, null, $iconCssClass ? array('class' => $iconCssClass) : array()); + echo $this->icon($icon, $iconTitle, $iconCssClass ? array('class' => $iconCssClass) : array()); } ?>nl2br($this->createTicketLinks($this->escapeComment($msg))) // TODO(ak): this allows only a[href] in messages, but plugin output allows more ?>

diff --git a/modules/monitoring/application/views/scripts/partials/host/objects-header.phtml b/modules/monitoring/application/views/scripts/partials/host/objects-header.phtml index 731ba1cf8..48141e2fd 100644 --- a/modules/monitoring/application/views/scripts/partials/host/objects-header.phtml +++ b/modules/monitoring/application/views/scripts/partials/host/objects-header.phtml @@ -20,7 +20,7 @@ if (! ($hostCount = count($objects))): return; endif ?> $host->host_name, $host->host_display_name ) ?> - hostFlags($host)) ?> + hostFlags($host) ?> diff --git a/modules/monitoring/application/views/scripts/partials/host/statusicons.phtml b/modules/monitoring/application/views/scripts/partials/host/statusicons.phtml deleted file mode 100644 index 4421c301f..000000000 --- a/modules/monitoring/application/views/scripts/partials/host/statusicons.phtml +++ /dev/null @@ -1,28 +0,0 @@ -object->host_handled && $this->object->host_state > 0) { - $icons[] = $this->icon('attention-alt', $this->translate('Unhandled')); -} - -if ($this->object->host_acknowledged && !$this->object->host_in_downtime) { - $icons[] = $this->icon('ok', $this->translate('Acknowledged')); -} - -if (! $this->object->host_notifications_enabled) { - $icons[] = $this->icon('bell-off-empty', $this->translate('Notifications Disabled')); -} - -if ($this->object->host_in_downtime) { - $icons[] = $this->icon('plug', $this->translate('In Downtime')); -} - -if (! $this->object->host_active_checks_enabled) { - if ($this->object->host_passive_checks_enabled) { - $icons[] = $this->icon('eye-off', $this->translate('Active Checks Disabled')); - } else { - $icons[] = $this->icon('eye-off', $this->translate('Active And Passive Checks Disabled')); - } -} - -?> \ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/partials/object/host-header.phtml b/modules/monitoring/application/views/scripts/partials/object/host-header.phtml index 8bc7b6c64..3bfb35030 100644 --- a/modules/monitoring/application/views/scripts/partials/object/host-header.phtml +++ b/modules/monitoring/application/views/scripts/partials/object/host-header.phtml @@ -19,7 +19,12 @@ use Icinga\Module\Monitoring\Object\Host; host_display_name !== $object->host_name): ?> (escape($object->host_name) ?>) - render('partials/host/statusicons.phtml') ?> + host_alias !== $object->host_display_name && $object->host_alias !== $object->host_name): ?> +
+ escape($this->translate('Alias', 'host') . ': ' . $object->host_alias) ?> +
+ + hostFlags($object) ?> host_address6 && $object->host_address6 !== $object->host_name): ?>
escape($object->host_address6) ?> diff --git a/modules/monitoring/application/views/scripts/partials/object/quick-actions.phtml b/modules/monitoring/application/views/scripts/partials/object/quick-actions.phtml new file mode 100644 index 000000000..5200923b9 --- /dev/null +++ b/modules/monitoring/application/views/scripts/partials/object/quick-actions.phtml @@ -0,0 +1,142 @@ +
+ +
diff --git a/modules/monitoring/application/views/scripts/partials/object/service-header.phtml b/modules/monitoring/application/views/scripts/partials/object/service-header.phtml index 93eb41f94..a293ff6da 100644 --- a/modules/monitoring/application/views/scripts/partials/object/service-header.phtml +++ b/modules/monitoring/application/views/scripts/partials/object/service-header.phtml @@ -20,7 +20,7 @@ use Icinga\Module\Monitoring\Object\Service; host_display_name !== $object->host_name): ?> (escape($object->host_name) ?>) - render('partials/host/statusicons.phtml') ?> + hostFlags($object) ?> host_address6 && $object->host_address6 !== $object->host_name): ?>
escape($object->host_address6) ?> @@ -49,7 +49,7 @@ use Icinga\Module\Monitoring\Object\Service; service_display_name !== $object->service_description): ?> (escape($object->service_description) ?>) - render('partials/service/statusicons.phtml') ?> + serviceFlags($object) ?> diff --git a/modules/monitoring/application/views/scripts/partials/service/objects-header.phtml b/modules/monitoring/application/views/scripts/partials/service/objects-header.phtml index 7512a3042..d342d874d 100644 --- a/modules/monitoring/application/views/scripts/partials/service/objects-header.phtml +++ b/modules/monitoring/application/views/scripts/partials/service/objects-header.phtml @@ -24,7 +24,7 @@ if (! ($serviceCount = count($objects))): return; endif ?> $service->host_display_name . ($service->host_state != 0 ? ' (' . Host::getStateText($service->host_state, true) . ')' : '') ) ?> - serviceFlags($service)) ?> + serviceFlags($service) ?> diff --git a/modules/monitoring/application/views/scripts/partials/service/statusicons.phtml b/modules/monitoring/application/views/scripts/partials/service/statusicons.phtml deleted file mode 100644 index 8859bf189..000000000 --- a/modules/monitoring/application/views/scripts/partials/service/statusicons.phtml +++ /dev/null @@ -1,28 +0,0 @@ -object->service_handled && $this->object->service_state > 0) { - $icons[] = $this->icon('attention-alt', $this->translate('Unhandled')); -} - -if ($this->object->service_acknowledged && !$this->object->service_in_downtime) { - $icons[] = $this->icon('ok', $this->translate('Acknowledged')); -} - -if (! $this->object->service_notifications_enabled) { - $icons[] = $this->icon('bell-off-empty', $this->translate('Notifications Disabled')); -} - -if ($this->object->service_in_downtime) { - $icons[] = $this->icon('plug', $this->translate('In Downtime')); -} - -if (! $this->object->service_active_checks_enabled) { - if ($this->object->service_passive_checks_enabled) { - $icons[] = $this->icon('eye-off', $this->translate('Active Checks Disabled')); - } else { - $icons[] = $this->icon('eye-off', $this->translate('Active And Passive Checks Disabled')); - } -} - -?> \ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/service/show.phtml b/modules/monitoring/application/views/scripts/service/show.phtml index 9dd139430..bc9c612b2 100644 --- a/modules/monitoring/application/views/scripts/service/show.phtml +++ b/modules/monitoring/application/views/scripts/service/show.phtml @@ -1,8 +1,8 @@ -
+
compact): ?> tabs ?> render('partials/object/service-header.phtml') ?> + render('partials/object/quick-actions.phtml') ?>
- render('partials/object/detail-content.phtml') ?> diff --git a/modules/monitoring/application/views/scripts/services/show.phtml b/modules/monitoring/application/views/scripts/services/show.phtml index 8e46f2e32..05f17b871 100644 --- a/modules/monitoring/application/views/scripts/services/show.phtml +++ b/modules/monitoring/application/views/scripts/services/show.phtml @@ -30,8 +30,8 @@ $acknowledgeLink, null, array( - 'icon' => 'ok', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'check' ) ) ?> @@ -62,8 +62,8 @@ $addCommentLink, null, array( - 'icon' => 'comment', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'comment-empty' ) ) ?> @@ -147,8 +147,8 @@ $sendCustomNotificationLink, null, array( - 'icon' => 'bell-alt', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'bell' ) ) ?> @@ -169,8 +169,8 @@ $processCheckResultAllLink, null, array( - 'icon' => 'reply', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'edit' ) ) ?> @@ -191,8 +191,8 @@ $rescheduleAllLink, null, array( - 'icon' => 'reschedule', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'calendar-empty' ) ) ?> diff --git a/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml b/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml index 568dc9062..9d1e195dc 100644 --- a/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml +++ b/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml @@ -80,7 +80,7 @@ $acknowledgement = $object->acknowledgement; array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'ok', + 'icon' => 'edit', 'title' => $this->translate( 'Acknowledge this problem, suppress all future notifications for it and tag it as being handled' ) diff --git a/modules/monitoring/application/views/scripts/show/components/checksource.phtml b/modules/monitoring/application/views/scripts/show/components/checksource.phtml index 8f069a09b..470b4de55 100644 --- a/modules/monitoring/application/views/scripts/show/components/checksource.phtml +++ b/modules/monitoring/application/views/scripts/show/components/checksource.phtml @@ -8,9 +8,9 @@ is_reachable !== null): ?> is_reachable ? $this->translate('is reachable') : $this->translate('is not reachable') ?> is_reachable) { - echo $this->icon('circle', $this->translate('Is reachable'), array('class' => 'check-source-reachable')); + echo $this->icon('circle', $this->translate('Is reachable'), array('class' => 'icon-stateful state-ok')); } else { - echo $this->icon('circle', $this->translate('Not reachable'), array('class' => 'check-source-not-reachable')); + echo $this->icon('circle', $this->translate('Not reachable'), array('class' => 'icon-stateful state-critical')); } ?> diff --git a/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml b/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml index 0ddcbcaa8..e37e30ac6 100644 --- a/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml +++ b/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml @@ -9,7 +9,7 @@ $activeChecksEnabled = (bool) $object->active_checks_enabled; state !== 99): ?> timeAgo($object->last_check) ?> next_update < time()): ?> - icon('circle', $this->translate('Check result is late'), array('class' => 'check-result-late')) ?> + icon('circle', $this->translate('Check result is late'), array('class' => 'icon-stateful state-critical')) ?> active_checks_enabled; array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'reschedule', + 'icon' => 'calendar-empty', 'title' => $this->translate( 'Schedule the next active check at a different time than the current one' ) @@ -51,7 +51,7 @@ $activeChecksEnabled = (bool) $object->active_checks_enabled; array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'reschedule', + 'icon' => 'calendar-empty', 'title' => $this->translate( 'Schedule the next active check at a different time than the current one' ) diff --git a/modules/monitoring/application/views/scripts/show/components/command.phtml b/modules/monitoring/application/views/scripts/show/components/command.phtml index 2ba5e7bc8..4a47c77b1 100644 --- a/modules/monitoring/application/views/scripts/show/components/command.phtml +++ b/modules/monitoring/application/views/scripts/show/components/command.phtml @@ -24,7 +24,7 @@ if ($showInstance): ?> array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'reply', + 'icon' => 'edit', 'title' => $title ) ); @@ -36,7 +36,7 @@ if ($showInstance): ?> array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'reply', + 'icon' => 'edit', 'title' => $title ) ); diff --git a/modules/monitoring/application/views/scripts/show/components/comments.phtml b/modules/monitoring/application/views/scripts/show/components/comments.phtml index 671c363d1..d8cbdc910 100644 --- a/modules/monitoring/application/views/scripts/show/components/comments.phtml +++ b/modules/monitoring/application/views/scripts/show/components/comments.phtml @@ -10,7 +10,7 @@ if ($this->hasPermission('monitoring/command/comment/add')) { array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'comment', + 'icon' => 'comment-empty', 'title' => $this->translate('Add a new comment to this host') ) ); @@ -22,7 +22,7 @@ if ($this->hasPermission('monitoring/command/comment/add')) { array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'comment', + 'icon' => 'comment-empty', 'title' => $this->translate('Add a new comment to this service') ) ); @@ -60,7 +60,8 @@ if (empty($object->comments) && ! $addLink) { $deleteButton->populate( array( 'comment_id' => $comment->id, - 'comment_is_service' => isset($comment->service_description) + 'comment_is_service' => isset($comment->service_description), + 'comment_name' => $comment->name ) ); echo $deleteButton; diff --git a/modules/monitoring/application/views/scripts/show/components/downtime.phtml b/modules/monitoring/application/views/scripts/show/components/downtime.phtml index 5655da4c4..ded3b9248 100644 --- a/modules/monitoring/application/views/scripts/show/components/downtime.phtml +++ b/modules/monitoring/application/views/scripts/show/components/downtime.phtml @@ -89,7 +89,8 @@ if (empty($object->comments) && ! $addLink) { $deleteButton->populate( array( 'downtime_id' => $downtime->id, - 'downtime_is_service' => $object->getType() === $object::TYPE_SERVICE + 'downtime_is_service' => $object->getType() === $object::TYPE_SERVICE, + 'downtime_name' => $downtime->name ) ); echo $deleteButton; diff --git a/modules/monitoring/application/views/scripts/show/components/notes.phtml b/modules/monitoring/application/views/scripts/show/components/notes.phtml index 25c1d8ecb..c4c12f80e 100644 --- a/modules/monitoring/application/views/scripts/show/components/notes.phtml +++ b/modules/monitoring/application/views/scripts/show/components/notes.phtml @@ -2,16 +2,10 @@ use Icinga\Web\Navigation\Navigation; -$navigation = new Navigation(); -//$navigation->load($object->getType() . '-note'); -foreach ($navigation as $item) { - $item->setObject($object); -} +/** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ -$notes = trim($object->getNotes()); -if ($notes) { - $navigation->addItem($notes); -} +$navigation = new Navigation(); +$notes = trim($object->notes); $links = $object->getNotesUrls(); if (! empty($links)) { @@ -35,11 +29,16 @@ if (! empty($links)) { } } -if ($navigation->isEmpty() || !$navigation->hasRenderableItems()) { +if (($navigation->isEmpty() || ! $navigation->hasRenderableItems()) && $notes === '') { return; } ?> - translate('Notes'); ?> - getRenderer()->setElementTag('td')->setCssClass('notes'); ?> + translate('Notes') ?> + + getRenderer() ?> + +

nl2br($this->escape($notes)) ?>

+ + \ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/show/components/notifications.phtml b/modules/monitoring/application/views/scripts/show/components/notifications.phtml index 8dbb43f93..3e8c66584 100644 --- a/modules/monitoring/application/views/scripts/show/components/notifications.phtml +++ b/modules/monitoring/application/views/scripts/show/components/notifications.phtml @@ -13,7 +13,7 @@ array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'bell-alt', + 'icon' => 'bell', 'title' => $this->translate( 'Send a custom notification to contacts responsible for this host' ) @@ -28,7 +28,7 @@ array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'bell-alt', + 'icon' => 'bell', 'title' => $this->translate( 'Send a custom notification to contacts responsible for this service' ) diff --git a/modules/monitoring/application/views/scripts/timeline/index.phtml b/modules/monitoring/application/views/scripts/timeline/index.phtml index 891a7f2b5..af3b406cc 100644 --- a/modules/monitoring/application/views/scripts/timeline/index.phtml +++ b/modules/monitoring/application/views/scripts/timeline/index.phtml @@ -13,9 +13,9 @@ if (! $beingExtended && !$this->compact): ?>

translate('Legend'); ?>

- - - + + +
@@ -84,11 +84,9 @@ $extrapolatedCircleWidth = $timeline->getExtrapolatedCircleWidth($timeInfo[1][$g ?>
-
getClass() ?>" style="getColor(), $extrapolatedCircleWidth ); ?>"> @@ -114,11 +112,10 @@ $extrapolatedCircleWidth = $timeline->getExtrapolatedCircleWidth($timeInfo[1][$g strtolower($labelAndColor['label']), $titleTime ), - 'class' => 'inner-circle', + 'class' => 'inner-circle ' . $timeInfo[1][$groupName]->getClass(), 'style' => sprintf( - 'width: %3$s; height: %3$s; background-color: %2$s; margin-top: -%1$Fem; margin-left: -%1$Fem;', + 'width: %2$s; height: %2$s; margin-top: -%1$Fem; margin-left: -%1$Fem;', (float) substr($circleWidth, 0, -2) / 2, - $timeInfo[1][$groupName]->getColor(), (string) $circleWidth ) ) diff --git a/modules/monitoring/configuration.php b/modules/monitoring/configuration.php index 01a6380ad..1b3433992 100644 --- a/modules/monitoring/configuration.php +++ b/modules/monitoring/configuration.php @@ -126,7 +126,7 @@ $section = $this->menuSection(N_('Problems'), array( 'SummaryNavigationItemRenderer', 'state' => 'critical' ), - 'icon' => 'block', + 'icon' => 'attention-circled', 'priority' => 20 )); $section->add(N_('Host Problems'), array( @@ -166,7 +166,7 @@ $section->add(N_('Current Downtimes'), array( * Overview Section */ $section = $this->menuSection(N_('Overview'), array( - 'icon' => 'sitemap', + 'icon' => 'binoculars', 'priority' => 30 )); $section->add(N_('Tactical Overview'), array( @@ -210,7 +210,7 @@ $section->add(N_('Downtimes'), array( * History Section */ $section = $this->menuSection(N_('History'), array( - 'icon' => 'rewind', + 'icon' => 'history', 'priority' => 90 )); $section->add(N_('Event Grid'), array( @@ -223,7 +223,7 @@ $section->add(N_('Event Overview'), array( )); $section->add(N_('Notifications'), array( 'priority' => 30, - 'url' => 'monitoring/list/notifications', + 'url' => 'monitoring/list/notifications?notification_timestamp>=-7%20days', )); $section->add(N_('Timeline'), array( 'priority' => 40, @@ -238,10 +238,6 @@ $section = $this->menuSection(N_('Reporting'), array( 'priority' => 100 )); -$section->add(N_('Alert Summary'), array( - 'url' => 'monitoring/alertsummary/index' -)); - /* * System Section */ diff --git a/modules/monitoring/doc/commandtransports.md b/modules/monitoring/doc/commandtransports.md index 680f34ca0..dd29e98dc 100644 --- a/modules/monitoring/doc/commandtransports.md +++ b/modules/monitoring/doc/commandtransports.md @@ -1,49 +1,83 @@ -# The commandtransports.ini configuration file +# External Command Transport Configuration -## Abstract +## Introduction -The commandtransports.ini defines how Icinga Web 2 accesses the command pipe of -your Icinga instance in order to submit external commands. Depending on the -config path (default: /etc/icingaweb2) of your Icinga Web 2 installation you can -find it under ./modules/monitoring/commandtransports.ini. +The `commandtransports.ini` defines how Icinga Web 2 transports commands to your Icinga instance in order to submit +external commands. By default, this file is located at `/etc/icingaweb2/modules/monitoring/commandtransports.ini`. -## Syntax +You can define multiple command transports in the `commandtransports.ini`. Every transport starts with a section header +containing its name, followed by the config directives for this transport in the standard INI-format. -You can define multiple command transports in the commandtransports.ini. Every -transport starts with a section header containing its name, followed by the -config directives for this transport in the standard INI-format. +Icinga Web 2 will try one transport after another to send a command until the command is successfully sent. +If [configured](#commandtransports-multiple-instances), Icinga Web 2 will take different instances into account. +The order in which Icinga Web 2 processes the configured transports is defined by the order of sections in +`commandtransports.ini`. -Icinga Web 2 will try one transport after another to send a command, depending -on the respective Icinga instance, until the command is successfully sent. The -order in which Icinga Web 2 processes the configured transports is defined by -the order of sections in the commandtransports.ini. +## Use the Icinga 2 API -## Using a local command pipe +If you're running Icinga 2 it's best to use the Icinga 2 API for transmitting external commands. + +First, please make sure that your server running Icinga Web 2 has the `PHP cURL` extension installed and enabled. + +Second, you have to enable the `api` feature on the Icinga 2 host where you want to send the commands to: + +``` +icinga2 feature enable api +``` + +Next, you have to create an ApiUser object for authenticating against the Icinga 2 API. This configuration also applies +to the host where you want to send the commands to. We recommend to create/edit the file +`/etc/icinga2/conf.d/api-users.conf`: + +``` +object ApiUser "web2" { + password = "bea11beb7b810ea9ce6ea" // Change this! + permissions = [ "actions/*", "objects/modify/hosts", "objects/modify/services", "objects/modify/icingaapplications" ] +} +``` + +The permissions `actions/*`, `objects/modify/hosts`, `objects/modify/services`, `objects/modify/icingaapplications` are +mandatory in order to submit all external commands from within Icinga Web 2. + +**Restart Icinga 2** for the changes to take effect. + +After that, you have to set up Icinga Web 2's `commandtransport.ini` to use the Icinga 2 API: + +``` +[icinga2] +transport = "api" +host = "127.0.0.1" // Icinga 2 host +port = "5665" +username = "web2" +password = "bea11beb7b810ea9ce6ea" // Change that! +``` + +## Use a Local Command Pipe A local Icinga instance requires the following directives: -```` +``` [icinga2] -transport = local -path = /var/run/icinga2/cmd/icinga2.cmd -```` +transport = local +path = /var/run/icinga2/cmd/icinga2.cmd +``` When sending commands to the Icinga instance, Icinga Web 2 opens the file found on the local filesystem underneath 'path' and writes the external command to it. -## Using SSH for accessing a remote command pipe +## Use SSH For a Remote Command Pipe A command pipe on a remote host's filesystem can be accessed by configuring a SSH based command transport and requires the following directives: -```` +``` [icinga2] -transport = remote -path = /var/run/icinga2/cmd/icinga2.cmd -host = example.tld -;port = 22 ; Optional. The default is 22 -user = icinga -```` +transport = remote +path = /var/run/icinga2/cmd/icinga2.cmd +host = example.tld +user = icinga +;port = 22 ; Optional. The default is 22 +``` To make this example work, you'll need to permit your web-server's user public-key based access to the defined remote host so that Icinga Web 2 can @@ -56,32 +90,31 @@ key file on the local filesystem that is used to access the remote host. To accomplish this, a new resource is required that is defined in your transport's configuration instead of a user: -```` +``` [icinga2] -transport = remote -path = /var/run/icinga2/cmd/icinga2.cmd -host = example.tld -;port = 22 ; Optional. The default is 22 -resource = example.tld-icinga2 -```` +transport = remote +path = /var/run/icinga2/cmd/icinga2.cmd +host = example.tld +resource = example.tld-icinga2 +;port = 22 ; Optional. The default is 22 +``` The resource's configuration needs to be put into the resources.ini file: -```` +``` [example.tld-icinga2] -type = ssh -user = icinga +type = ssh +user = icinga private_key = /etc/icingaweb2/ssh/icinga -```` +``` -## Configuring transports for different Icinga instances +## Configure Transports for Different Icinga Instances -If there are multiple but different Icinga instances writing to your IDO you can -define which transport belongs to which Icinga instance by providing the -directive 'instance'. This directive should contain the name of the Icinga +If there are multiple but different Icinga instances writing to your IDO, you can define which transport belongs to +which Icinga instance by providing the directive `instance`. This directive should contain the name of the Icinga instance you want to assign to the transport: -```` +``` [icinga1] ... instance = icinga1 @@ -89,8 +122,7 @@ instance = icinga1 [icinga2] ... instance = icinga2 -```` +``` -Associating a transport to a specific Icinga instance causes this transport to -be used to send commands to the linked instance only. Transports without a -linked Icinga instance are utilized to send commands to all instances. \ No newline at end of file +Associating a transport to a specific Icinga instance causes this transport to be used to send commands to the linked +instance only. Transports without a linked Icinga instance are used to send commands to all instances. diff --git a/modules/monitoring/doc/configuration.md b/modules/monitoring/doc/configuration.md index 5c3a23aa4..da63d8bbd 100644 --- a/modules/monitoring/doc/configuration.md +++ b/modules/monitoring/doc/configuration.md @@ -5,12 +5,12 @@ Apart from its web configuration capabilities, the local configuration is stored in `/etc/icingaweb2` by default (depending on your config setup). - Location | File | Description - ------------------------------|-----------------------|--------------------------- - modules/monitoring | Directory | `monitoring` module specific configuration - modules/monitoring | config.ini | Security settings (e.g. protected custom vars) for the `monitoring` module - modules/monitoring | backends.ini | Backend type and resources (e.g. Icinga IDO DB) - modules/monitoring | [commandtransports.ini](commandtransports.md#commandtransports) | Command transports for specific Icinga instances +| Location | File | Description | +| --------------------- | ----------------------------------------------------------------- | ----------- | +| modules/monitoring | Directory | `monitoring` module specific configuration | +| modules/monitoring | config.ini | Security settings (e.g. protected custom vars) for the `monitoring` module | +| modules/monitoring | backends.ini | Backend type and resources (e.g. Icinga IDO DB) | +| modules/monitoring | [commandtransports.ini](commandtransports.md#commandtransports) | Command transports for specific Icinga instances | diff --git a/modules/monitoring/doc/security.md b/modules/monitoring/doc/security.md index cce3727a8..74ca69519 100644 --- a/modules/monitoring/doc/security.md +++ b/modules/monitoring/doc/security.md @@ -13,7 +13,7 @@ commands when using the monitoring module. | Name | Permits | -|---------------------------------------------|-----------------------------------------------------------------------------| +| ------------------------------------------- | --------------------------------------------------------------------------- | | monitoring/command/* | Allow all commands | | monitoring/command/schedule-check | Allow scheduling host and service checks' | | monitoring/command/acknowledge-problem | Allow acknowledging host and service problems | @@ -33,7 +33,7 @@ The monitoring module allows filtering objects: | Keys | Restricts | -|----------------------------|-----------------------------------------------| +| ---------------------------|---------------------------------------------- | | monitoring/filter/objects | Applies a filter to all hosts and services | @@ -48,7 +48,7 @@ The following filter column names are available in filter expressions: | Column | -|--------------------------------------------------------------| +| ------------------------------------------------------------ | | instance_name | | host_name | | hostgroup_name | diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/AllcontactsQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/AllcontactsQuery.php index ae0276ddc..09779b66e 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/AllcontactsQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/AllcontactsQuery.php @@ -27,7 +27,7 @@ class AllcontactsQuery extends IdoQuery 'contact_notify_service_critical' => 'c.notify_service_critical', 'contact_notify_service_unknown' => 'c.notify_service_unknown', 'contact_notify_service_flapping' => 'c.notify_service_flapping', - 'contact_notify_service_downtime' => 'c.notify_service_recovery', + 'contact_notify_service_downtime' => 'c.notify_service_downtime', 'contact_notify_host_recovery' => 'c.notify_host_recovery', 'contact_notify_host_down' => 'c.notify_host_down', 'contact_notify_host_unreachable' => 'c.notify_host_unreachable', diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/CommentQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/CommentQuery.php index 2fd3fe15f..1d723a0c1 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/CommentQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/CommentQuery.php @@ -23,6 +23,7 @@ class CommentQuery extends IdoQuery 'comment_expiration' => 'c.comment_expiration', 'comment_internal_id' => 'c.comment_internal_id', 'comment_is_persistent' => 'c.comment_is_persistent', + 'comment_name' => 'c.comment_name', 'comment_timestamp' => 'c.comment_timestamp', 'comment_type' => 'c.comment_type', 'instance_name' => 'c.instance_name', @@ -85,6 +86,9 @@ class CommentQuery extends IdoQuery */ protected function joinBaseTables() { + if (version_compare($this->getIdoVersion(), '1.14.0', '<')) { + $this->columnMap['comments']['comment_name'] = '(NULL)'; + } $this->commentQuery = $this->db->select(); $this->select->from( array('c' => $this->commentQuery), diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactQuery.php index c71975b8a..196c50cd2 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactQuery.php @@ -48,7 +48,7 @@ class ContactQuery extends IdoQuery 'contact_notify_service_critical' => 'c.notify_service_critical', 'contact_notify_service_unknown' => 'c.notify_service_unknown', 'contact_notify_service_flapping' => 'c.notify_service_flapping', - 'contact_notify_service_downtime' => 'c.notify_service_recovery', + 'contact_notify_service_downtime' => 'c.notify_service_downtime', 'contact_notify_host_recovery' => 'c.notify_host_recovery', 'contact_notify_host_down' => 'c.notify_host_down', 'contact_notify_host_unreachable' => 'c.notify_host_unreachable', diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php index af665cb35..f45c21e01 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php @@ -87,7 +87,7 @@ class ContactgroupQuery extends IdoQuery array('cgm' => $this->prefix . 'contactgroup_members'), 'cgm.contactgroup_id = cg.contactgroup_id', array() - )->join( + )->joinLeft( array('co' => $this->prefix . 'objects'), 'co.object_id = cgm.contact_object_id AND co.is_active = 1 AND co.objecttype_id = 10', array() diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/CustomvarQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/CustomvarQuery.php index 9ae95b204..c97cc916a 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/CustomvarQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/CustomvarQuery.php @@ -3,6 +3,8 @@ namespace Icinga\Module\Monitoring\Backend\Ido\Query; +use Icinga\Application\Config; + class CustomvarQuery extends IdoQuery { protected $columnMap = array( @@ -44,8 +46,14 @@ class CustomvarQuery extends IdoQuery $this->columnMap['customvariablestatus']['is_json'] = '(0)'; } + if (! (bool) Config::module('monitoring')->get('ido', 'use_customvar_status_table', true)) { + $table = 'customvariables'; + } else { + $table = 'customvariablestatus'; + } + $this->select->from( - array('cvs' => $this->prefix . 'customvariablestatus'), + array('cvs' => $this->prefix . $table), array() )->join( array('cvo' => $this->prefix . 'objects'), diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/DowntimeQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/DowntimeQuery.php index ae0c08d9c..2c85c1547 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/DowntimeQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/DowntimeQuery.php @@ -27,6 +27,7 @@ class DowntimeQuery extends IdoQuery 'downtime_is_fixed' => 'd.downtime_is_fixed', 'downtime_is_flexible' => 'd.downtime_is_flexible', 'downtime_is_in_effect' => 'd.downtime_is_in_effect', + 'downtime_name' => 'd.downtime_name', 'downtime_scheduled_end' => 'd.downtime_scheduled_end', 'downtime_scheduled_start' => 'd.downtime_scheduled_start', 'downtime_start' => 'd.downtime_start', @@ -90,6 +91,9 @@ class DowntimeQuery extends IdoQuery */ protected function joinBaseTables() { + if (version_compare($this->getIdoVersion(), '1.14.0', '<')) { + $this->columnMap['downtimes']['downtime_name'] = '(NULL)'; + } $this->downtimeQuery = $this->db->select(); $this->select->from( array('d' => $this->downtimeQuery), diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/EventhistoryQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/EventhistoryQuery.php index 542a45b9d..c8ee0b8a3 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/EventhistoryQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/EventhistoryQuery.php @@ -56,7 +56,6 @@ class EventhistoryQuery extends IdoQuery 'type', 'state', 'object_type', - 'object_id', 'host_name', 'service_description', 'host_display_name', @@ -68,7 +67,7 @@ class EventhistoryQuery extends IdoQuery $this->createSubQuery('Downtimeendhistory', $columns), $this->createSubQuery('Commenthistory', $columns), $this->createSubQuery('Commentdeletionhistory', $columns), - $this->createSubQuery('Notification', $columns) + $this->createSubQuery('Notificationhistory', $columns) ); $sub = $this->db->select()->union($this->subQueries, Zend_Db_Select::SQL_UNION_ALL); $this->select->from(array('eh' => $sub), array()); diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostcommentQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostcommentQuery.php index 056eda400..30cd414ed 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostcommentQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostcommentQuery.php @@ -34,6 +34,7 @@ class HostcommentQuery extends IdoQuery 'comment_expiration' => 'CASE c.expires WHEN 1 THEN UNIX_TIMESTAMP(c.expiration_time) ELSE NULL END', 'comment_internal_id' => 'c.internal_comment_id', 'comment_is_persistent' => 'c.is_persistent', + 'comment_name' => 'c.name', 'comment_timestamp' => 'UNIX_TIMESTAMP(c.comment_time)', 'comment_type' => "CASE c.entry_type WHEN 1 THEN 'comment' WHEN 2 THEN 'downtime' WHEN 3 THEN 'flapping' WHEN 4 THEN 'ack' END", 'host' => 'ho.name1 COLLATE latin1_general_ci', @@ -72,6 +73,9 @@ class HostcommentQuery extends IdoQuery */ protected function joinBaseTables() { + if (version_compare($this->getIdoVersion(), '1.14.0', '<')) { + $this->columnMap['comments']['comment_name'] = '(NULL)'; + } $this->select->from( array('c' => $this->prefix . 'comments'), array() diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostdowntimeQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostdowntimeQuery.php index b3be3420b..3bea210de 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostdowntimeQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostdowntimeQuery.php @@ -38,6 +38,7 @@ class HostdowntimeQuery extends IdoQuery 'downtime_is_fixed' => 'sd.is_fixed', 'downtime_is_flexible' => 'CASE WHEN sd.is_fixed = 0 THEN 1 ELSE 0 END', 'downtime_is_in_effect' => 'sd.is_in_effect', + 'downtime_name' => 'sd.name', 'downtime_scheduled_end' => 'UNIX_TIMESTAMP(sd.scheduled_end_time)', 'downtime_scheduled_start' => 'UNIX_TIMESTAMP(sd.scheduled_start_time)', 'downtime_start' => 'UNIX_TIMESTAMP(CASE WHEN UNIX_TIMESTAMP(sd.trigger_time) > 0 then sd.trigger_time ELSE sd.scheduled_start_time END)', @@ -78,6 +79,9 @@ class HostdowntimeQuery extends IdoQuery */ protected function joinBaseTables() { + if (version_compare($this->getIdoVersion(), '1.14.0', '<')) { + $this->columnMap['downtimes']['downtime_name'] = '(NULL)'; + } $this->select->from( array('sd' => $this->prefix . 'scheduleddowntime'), array() diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostnotificationQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostnotificationQuery.php index 0ee815df8..9b7d3e5c8 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostnotificationQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostnotificationQuery.php @@ -17,51 +17,35 @@ class HostnotificationQuery extends IdoQuery * {@inheritdoc} */ protected $columnMap = array( + 'contactnotifications' => array( + 'notification_contact_name' => 'co.name1' + ), + 'hostgroups' => array( + 'hostgroup_name' => 'hgo.name1' + ), + 'hosts' => array( + 'host_display_name' => 'h.display_name COLLATE latin1_general_ci' + ), + 'history' => array( + 'output' => null, + 'state' => 'hn.state', + 'timestamp' => 'UNIX_TIMESTAMP(hn.start_time)', + 'type' => '(\'notify\')' + ), 'instances' => array( 'instance_name' => 'i.instance_name' ), 'notifications' => array( - 'notification_output' => 'hn.output', - 'notification_start_time' => 'UNIX_TIMESTAMP(hn.start_time)', - 'notification_state' => 'hn.state', - 'notification_object_id' => 'hn.object_id', - 'host' => 'ho.name1 COLLATE latin1_general_ci', 'host_name' => 'ho.name1', + 'notification_output' => 'hn.output', + 'notification_state' => 'hn.state', + 'notification_timestamp' => 'UNIX_TIMESTAMP(hn.start_time)', 'object_type' => '(\'host\')' ), - 'history' => array( - 'type' => "('notify')", - 'timestamp' => 'UNIX_TIMESTAMP(hn.start_time)', - 'object_id' => 'hn.object_id', - 'state' => 'hn.state', - 'output' => null - ), - 'contactnotifications' => array( - 'contact' => 'cno.name1 COLLATE latin1_general_ci', - 'notification_contact_name' => 'cno.name1', - 'contact_object_id' => 'cno.object_id' - ), - 'acknowledgements' => array( - 'acknowledgement_entry_time' => 'UNIX_TIMESTAMP(a.entry_time)', - 'acknowledgement_author_name' => 'a.author_name', - 'acknowledgement_comment_data' => 'a.comment_data' - ), - 'hostgroups' => array( - 'hostgroup' => 'hgo.name1 COLLATE latin1_general_ci', - 'hostgroup_alias' => 'hg.alias COLLATE latin1_general_ci', - 'hostgroup_name' => 'hgo.name1' - ), - 'hosts' => array( - 'host_alias' => 'h.alias', - 'host_display_name' => 'h.display_name COLLATE latin1_general_ci' - ), 'servicegroups' => array( - 'servicegroup' => 'sgo.name1 COLLATE latin1_general_ci', - 'servicegroup_name' => 'sgo.name1', - 'servicegroup_alias' => 'sg.alias COLLATE latin1_general_ci' + 'servicegroup_name' => 'sgo.name1' ), 'services' => array( - 'service' => 'so.name2 COLLATE latin1_general_ci', 'service_description' => 'so.name2', 'service_display_name' => 's.display_name COLLATE latin1_general_ci', 'service_host_name' => 'so.name1' @@ -88,26 +72,14 @@ class HostnotificationQuery extends IdoQuery switch ($this->ds->getDbType()) { case 'mysql': $concattedContacts = "GROUP_CONCAT(" - . "DISTINCT cno.name1 ORDER BY cno.name1 SEPARATOR ', '" + . "DISTINCT co.name1 ORDER BY co.name1 SEPARATOR ', '" . ") COLLATE latin1_general_ci"; break; case 'pgsql': // TODO: Find a way to order the contact alias list: - $concattedContacts = "ARRAY_TO_STRING(ARRAY_AGG(DISTINCT cno.name1), ', ')"; - break; - case 'oracle': - // TODO: This is only valid for Oracle >= 11g Release 2 - $concattedContacts = "LISTAGG(cno.name1, ', ') WITHIN GROUP (ORDER BY cno.name1)"; - // Alternatives: - // - // RTRIM(XMLAGG(XMLELEMENT(e, column_name, ',').EXTRACT('//text()')), - // - // not supported and not documented but works since 10.1, - // however it is NOT always present: - // WM_CONCAT(c.alias) + $concattedContacts = "ARRAY_TO_STRING(ARRAY_AGG(DISTINCT co.name1), ', ')"; break; } - $this->columnMap['history']['output'] = "('[' || $concattedContacts || '] ' || hn.output)"; $this->select->from( @@ -140,20 +112,8 @@ class HostnotificationQuery extends IdoQuery array() ); $this->select->joinLeft( - array('cno' => $this->prefix . 'objects'), - 'cno.object_id = cn.contact_object_id', - array() - ); - } - - /** - * Join acknowledgements - */ - protected function joinAcknowledgements() - { - $this->select->joinLeft( - array('a' => $this->prefix . 'acknowledgements'), - 'a.object_id = hn.object_id', + array('co' => $this->prefix . 'objects'), + 'co.object_id = cn.contact_object_id AND co.is_active = 1 AND co.objecttype_id = 10', array() ); } @@ -244,6 +204,8 @@ class HostnotificationQuery extends IdoQuery */ public function getGroup() { + $group = array(); + if ( $this->hasJoinedVirtualTable('history') || $this->hasJoinedVirtualTable('services') @@ -251,10 +213,10 @@ class HostnotificationQuery extends IdoQuery ) { $group = array('hn.notification_id', 'ho.object_id'); if ($this->hasJoinedVirtualTable('contactnotifications') && !$this->hasJoinedVirtualTable('history')) { - $group[] = 'cno.object_id'; + $group[] = 'co.object_id'; } } elseif ($this->hasJoinedVirtualTable('contactnotifications')) { - $group = array('hn.notification_id', 'cno.object_id', 'ho.object_id'); + $group = array('hn.notification_id', 'co.object_id', 'ho.object_id'); } if (! empty($group)) { @@ -262,10 +224,6 @@ class HostnotificationQuery extends IdoQuery $group[] = 'h.host_id'; } - if ($this->hasJoinedVirtualTable('acknowledgements')) { - $group[] = 'a.acknowledgement_id'; - } - if ($this->hasJoinedVirtualTable('instances')) { $group[] = 'i.instance_id'; } diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HoststatusQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HoststatusQuery.php index ea466bf37..dd73dd3e5 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HoststatusQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HoststatusQuery.php @@ -148,11 +148,6 @@ class HoststatusQuery extends IdoQuery ELSE 4 END END - END - + - CASE WHEN hs.state_type = 1 - THEN 8 - ELSE 0 END', 'host_state' => 'CASE WHEN hs.has_been_checked = 0 OR hs.has_been_checked IS NULL THEN 99 ELSE hs.current_state END', 'host_state_type' => 'hs.state_type', diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php index 9950fdbcd..8729cf25c 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php @@ -12,29 +12,21 @@ use Icinga\Data\Filter\Filter; */ class NotificationQuery extends IdoQuery { + /** + * {@inheritdoc} + */ + protected $allowCustomVars = true; + /** * {@inheritdoc} */ protected $columnMap = array( 'notifications' => array( - 'notification_state' => 'n.notification_state', - 'notification_start_time' => 'n.notification_start_time', + 'instance_name' => 'n.instance_name', 'notification_contact_name' => 'n.notification_contact_name', 'notification_output' => 'n.notification_output', - 'notification_object_id' => 'n.notification_object_id', - 'contact_object_id' => 'n.contact_object_id', - 'acknowledgement_entry_time' => 'n.acknowledgement_entry_time', - 'acknowledgement_author_name' => 'n.acknowledgement_author_name', - 'acknowledgement_comment_data' => 'n.acknowledgement_comment_data', - 'object_type' => 'n.object_type', - 'instance_name' => 'n.instance_name' - ), - 'history' => array( - 'type' => 'n.type', - 'timestamp' => 'n.timestamp', - 'object_id' => 'n.object_id', - 'state' => 'n.state', - 'output' => 'n.output' + 'notification_state' => 'n.notification_state', + 'notification_timestamp' => 'n.notification_timestamp' ), 'hosts' => array( 'host_display_name' => 'n.host_display_name', @@ -61,13 +53,6 @@ class NotificationQuery extends IdoQuery */ protected $subQueries = array(); - /** - * Whether to additionally select all history columns - * - * @var bool - */ - protected $fetchHistoryColumns = false; - /** * {@inheritdoc} */ @@ -81,33 +66,20 @@ class NotificationQuery extends IdoQuery $this->joinedVirtualTables['notifications'] = true; } - /** - * Join history related columns and tables - */ - protected function joinHistory() - { - // TODO: Ensure that one is selecting the history columns first... - $this->fetchHistoryColumns = true; - $this->requireVirtualTable('hosts'); - $this->requireVirtualTable('services'); - } - /** * Join hosts */ protected function joinHosts() { - $columns = array_keys($this->columnMap['hosts']); + $columns = $this->desiredColumns; + $columns = array_combine($columns, $columns); foreach ($this->columnMap['services'] as $column => $_) { - $columns[$column] = new Zend_Db_Expr('NULL'); - } - if ($this->fetchHistoryColumns) { - $columns = array_merge($columns, array_keys($this->columnMap['history'])); - $columns[] = 'object_type'; - } else { - $columns = array_merge($columns, array_keys($this->columnMap['notifications'])); + if (isset($columns[$column])) { + $columns[$column] = new Zend_Db_Expr('NULL'); + } } $hosts = $this->createSubQuery('hostnotification', $columns); + $hosts->setIsSubQuery(true); $this->subQueries[] = $hosts; $this->notificationQuery->union(array($hosts), Zend_Db_Select::SQL_UNION_ALL); } @@ -117,14 +89,8 @@ class NotificationQuery extends IdoQuery */ protected function joinServices() { - $columns = array_keys($this->columnMap['hosts'] + $this->columnMap['services']); - if ($this->fetchHistoryColumns) { - $columns = array_merge($columns, array_keys($this->columnMap['history'])); - $columns[] = 'object_type'; - } else { - $columns = array_merge($columns, array_keys($this->columnMap['notifications'])); - } - $services = $this->createSubQuery('servicenotification', $columns); + $services = $this->createSubQuery('servicenotification', $this->desiredColumns); + $services->setIsSubQuery(true); $this->subQueries[] = $services; $this->notificationQuery->union(array($services), Zend_Db_Select::SQL_UNION_ALL); } @@ -132,15 +98,12 @@ class NotificationQuery extends IdoQuery /** * {@inheritdoc} */ - public function allowsCustomVars() + public function addFilter(Filter $filter) { - foreach ($this->subQueries as $query) { - if (! $query->allowsCustomVars()) { - return false; - } + foreach ($this->subQueries as $sub) { + $sub->applyFilter(clone $filter); } - - return true; + return $this; } /** @@ -165,25 +128,4 @@ class NotificationQuery extends IdoQuery } return $this; } - - /** - * {@inheritdoc} - */ - public function addFilter(Filter $filter) - { - foreach ($this->subQueries as $sub) { - $sub->applyFilter(clone $filter); - } - return $this; - } - - /** - * {@inheritdoc} - */ - public function columns(array $columns) - { - parent::columns($columns); - $this->requireVirtualTable('hosts'); - $this->requireVirtualTable('services'); - } } diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationhistoryQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationhistoryQuery.php index dad75d5e0..fd83e571f 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationhistoryQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationhistoryQuery.php @@ -3,94 +3,134 @@ namespace Icinga\Module\Monitoring\Backend\Ido\Query; +use Zend_Db_Expr; +use Zend_Db_Select; +use Icinga\Data\Filter\Filter; + +/** + * Query for host and service notification history + */ class NotificationhistoryQuery extends IdoQuery { + /** + * {@inheritdoc} + */ + protected $allowCustomVars = true; + + /** + * {@inheritdoc} + */ protected $columnMap = array( 'history' => array( - 'state_time' => 'n.start_time', - 'timestamp' => 'UNIX_TIMESTAMP(n.start_time)', - 'raw_timestamp' => 'n.start_time', - 'object_id' => 'n.object_id', - 'type' => "('notify')", - 'state' => 'n.state', - 'state_type' => '(NULL)', - 'output' => null, - 'attempt' => '(NULL)', - 'max_attempts' => '(NULL)', - - 'host' => 'o.name1 COLLATE latin1_general_ci', - 'service' => 'o.name2 COLLATE latin1_general_ci', - 'host_name' => 'o.name1', - 'service_description' => 'o.name2', - 'object_type' => "CASE WHEN o.objecttype_id = 1 THEN 'host' ELSE 'service' END" + 'object_type' => 'n.object_type', + 'output' => 'n.output', + 'state' => 'n.state', + 'timestamp' => 'n.timestamp', + 'type' => "('notify')" + ), + 'hosts' => array( + 'host_display_name' => 'n.host_display_name', + 'host_name' => 'n.host_name' + ), + 'services' => array( + 'service_description' => 'n.service_description', + 'service_display_name' => 'n.service_display_name', + 'service_host_name' => 'n.service_host_name' ) ); - public function whereToSql($col, $sign, $expression) - { - if ($col === 'UNIX_TIMESTAMP(n.start_time)') { - return 'n.start_time ' . $sign . ' ' . $this->timestampForSql($this->valueToTimestamp($expression)); - } else { - return parent::whereToSql($col, $sign, $expression); - } - } + /** + * The union + * + * @var Zend_Db_Select + */ + protected $notificationQuery; + /** + * Subqueries used for the notification query + * + * @var IdoQuery[] + */ + protected $subQueries = array(); + + /** + * {@inheritdoc} + */ protected function joinBaseTables() { - switch ($this->ds->getDbType()) { - case 'mysql': - $concattedContacts = "GROUP_CONCAT(co.name1 ORDER BY co.name1 SEPARATOR ', ') COLLATE latin1_general_ci"; - break; - case 'pgsql': - // TODO: Find a way to order the contact alias list: - $concattedContacts = "ARRAY_TO_STRING(ARRAY_AGG(co.name1), ', ')"; - break; - case 'oracle': - // TODO: This is only valid for Oracle >= 11g Release 2 - $concattedContacts = "LISTAGG(co.name1, ', ') WITHIN GROUP (ORDER BY co.name1)"; - // Alternatives: - // - // RTRIM(XMLAGG(XMLELEMENT(e, column_name, ',').EXTRACT('//text()')), - // - // not supported and not documented but works since 10.1, - // however it is NOT always present: - // WM_CONCAT(c.alias) - break; - } - - $this->columnMap['history']['output'] = "('[' || $concattedContacts || '] ' || n.output)"; - + $this->notificationQuery = $this->db->select(); $this->select->from( - array('o' => $this->prefix . 'objects'), + array('n' => $this->notificationQuery), array() - )->join( - array('n' => $this->prefix . 'notifications'), - 'o.' . $this->object_id . ' = n.' . $this->object_id . ' AND o.is_active = 1', - array() - )->join( - array('cn' => $this->prefix . 'contactnotifications'), - 'cn.notification_id = n.notification_id', - array() - )->joinLeft( - array('co' => $this->prefix . 'objects'), - 'cn.contact_object_id = co.object_id', - array() - )->joinLeft( - array('c' => $this->prefix . 'contacts'), - 'co.object_id = c.contact_object_id', - array() - )->group('cn.notification_id'); - - // TODO: hmmmm... - if ($this->ds->getDbType() === 'pgsql') { - $this->select->group('n.object_id') - ->group('n.start_time') - ->group('n.output') - ->group('n.state') - ->group('o.objecttype_id'); - } - - $this->joinedVirtualTables = array('history' => true); + ); + $this->joinedVirtualTables['history'] = true; } + /** + * Join hosts + */ + protected function joinHosts() + { + $columns = $this->desiredColumns; + $columns = array_combine($columns, $columns); + foreach ($this->columnMap['services'] as $column => $_) { + if (isset($columns[$column])) { + $columns[$column] = new Zend_Db_Expr('NULL'); + } + } + if (isset($columns['type'])) { + unset($columns['type']); + } + $hosts = $this->createSubQuery('hostnotification', $columns); + $this->subQueries[] = $hosts; + $this->notificationQuery->union(array($hosts), Zend_Db_Select::SQL_UNION_ALL); + } + + /** + * Join services + */ + protected function joinServices() + { + $columns = array_flip($this->desiredColumns); + if (isset($columns['type'])) { + unset($columns['type']); + } + $services = $this->createSubQuery('servicenotification', array_flip($columns)); + $this->subQueries[] = $services; + $this->notificationQuery->union(array($services), Zend_Db_Select::SQL_UNION_ALL); + } + + /** + * {@inheritdoc} + */ + public function addFilter(Filter $filter) + { + foreach ($this->subQueries as $sub) { + $sub->applyFilter(clone $filter); + } + return $this; + } + + /** + * {@inheritdoc} + */ + public function order($columnOrAlias, $dir = null) + { + foreach ($this->subQueries as $sub) { + $sub->requireColumn($columnOrAlias); + } + return parent::order($columnOrAlias, $dir); + } + + /** + * {@inheritdoc} + */ + public function where($condition, $value = null) + { + $this->requireColumn($condition); + foreach ($this->subQueries as $sub) { + $sub->where($condition, $value); + } + return $this; + } } diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicecommentQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicecommentQuery.php index bebc6b7fd..2225058fd 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicecommentQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicecommentQuery.php @@ -34,6 +34,7 @@ class ServicecommentQuery extends IdoQuery 'comment_expiration' => 'CASE c.expires WHEN 1 THEN UNIX_TIMESTAMP(c.expiration_time) ELSE NULL END', 'comment_internal_id' => 'c.internal_comment_id', 'comment_is_persistent' => 'c.is_persistent', + 'comment_name' => 'c.name', 'comment_timestamp' => 'UNIX_TIMESTAMP(c.comment_time)', 'comment_type' => "CASE c.entry_type WHEN 1 THEN 'comment' WHEN 2 THEN 'downtime' WHEN 3 THEN 'flapping' WHEN 4 THEN 'ack' END", 'host' => 'so.name1 COLLATE latin1_general_ci', @@ -77,6 +78,9 @@ class ServicecommentQuery extends IdoQuery */ protected function joinBaseTables() { + if (version_compare($this->getIdoVersion(), '1.14.0', '<')) { + $this->columnMap['comments']['comment_name'] = '(NULL)'; + } $this->select->from( array('c' => $this->prefix . 'comments'), array() diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicedowntimeQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicedowntimeQuery.php index c0b0fca2a..2c370bea9 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicedowntimeQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicedowntimeQuery.php @@ -38,6 +38,7 @@ class ServicedowntimeQuery extends IdoQuery 'downtime_is_fixed' => 'sd.is_fixed', 'downtime_is_flexible' => 'CASE WHEN sd.is_fixed = 0 THEN 1 ELSE 0 END', 'downtime_is_in_effect' => 'sd.is_in_effect', + 'downtime_name' => 'sd.name', 'downtime_scheduled_end' => 'UNIX_TIMESTAMP(sd.scheduled_end_time)', 'downtime_scheduled_start' => 'UNIX_TIMESTAMP(sd.scheduled_start_time)', 'downtime_start' => 'UNIX_TIMESTAMP(CASE WHEN UNIX_TIMESTAMP(sd.trigger_time) > 0 then sd.trigger_time ELSE sd.scheduled_start_time END)', @@ -83,6 +84,9 @@ class ServicedowntimeQuery extends IdoQuery */ protected function joinBaseTables() { + if (version_compare($this->getIdoVersion(), '1.14.0', '<')) { + $this->columnMap['downtimes']['downtime_name'] = '(NULL)'; + } $this->select->from( array('sd' => $this->prefix . 'scheduleddowntime'), array() diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicenotificationQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicenotificationQuery.php index 0e7954746..d990bb0fb 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicenotificationQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicenotificationQuery.php @@ -17,52 +17,35 @@ class ServicenotificationQuery extends IdoQuery * {@inheritdoc} */ protected $columnMap = array( + 'contactnotifications' => array( + 'notification_contact_name' => 'co.name1' + ), + 'history' => array( + 'output' => null, + 'state' => 'sn.state', + 'timestamp' => 'UNIX_TIMESTAMP(sn.start_time)', + 'type' => '(\'notify\')' + ), + 'hostgroups' => array( + 'hostgroup_name' => 'hgo.name1' + ), + 'hosts' => array( + 'host_display_name' => 'h.display_name COLLATE latin1_general_ci' + ), 'instances' => array( 'instance_name' => 'i.instance_name' ), 'notifications' => array( - 'notification_output' => 'sn.output', - 'notification_start_time' => 'UNIX_TIMESTAMP(sn.start_time)', - 'notification_state' => 'sn.state', - 'notification_object_id' => 'sn.object_id', - 'host' => 'so.name1 COLLATE latin1_general_ci', 'host_name' => 'so.name1', + 'notification_output' => 'sn.output', + 'notification_state' => 'sn.state', + 'notification_timestamp' => 'UNIX_TIMESTAMP(sn.start_time)', 'object_type' => '(\'service\')', - 'service' => 'so.name2 COLLATE latin1_general_ci', 'service_description' => 'so.name2', - 'service_host' => 'so.name1 COLLATE latin1_general_ci', 'service_host_name' => 'so.name1' ), - 'history' => array( - 'type' => "('notify')", - 'timestamp' => 'UNIX_TIMESTAMP(sn.start_time)', - 'object_id' => 'sn.object_id', - 'state' => 'sn.state', - 'output' => null - ), - 'contactnotifications' => array( - 'contact' => 'cno.name1 COLLATE latin1_general_ci', - 'notification_contact_name' => 'cno.name1', - 'contact_object_id' => 'cno.object_id' - ), - 'acknowledgements' => array( - 'acknowledgement_entry_time' => 'UNIX_TIMESTAMP(a.entry_time)', - 'acknowledgement_author_name' => 'a.author_name', - 'acknowledgement_comment_data' => 'a.comment_data' - ), - 'hostgroups' => array( - 'hostgroup' => 'hgo.name1 COLLATE latin1_general_ci', - 'hostgroup_alias' => 'hg.alias COLLATE latin1_general_ci', - 'hostgroup_name' => 'hgo.name1' - ), - 'hosts' => array( - 'host_alias' => 'h.alias', - 'host_display_name' => 'h.display_name COLLATE latin1_general_ci' - ), 'servicegroups' => array( - 'servicegroup' => 'sgo.name1 COLLATE latin1_general_ci', - 'servicegroup_name' => 'sgo.name1', - 'servicegroup_alias' => 'sg.alias COLLATE latin1_general_ci' + 'servicegroup_name' => 'sgo.name1' ), 'services' => array( 'service_display_name' => 's.display_name COLLATE latin1_general_ci' @@ -89,26 +72,14 @@ class ServicenotificationQuery extends IdoQuery switch ($this->ds->getDbType()) { case 'mysql': $concattedContacts = "GROUP_CONCAT(" - . "DISTINCT cno.name1 ORDER BY cno.name1 SEPARATOR ', '" + . "DISTINCT co.name1 ORDER BY co.name1 SEPARATOR ', '" . ") COLLATE latin1_general_ci"; break; case 'pgsql': // TODO: Find a way to order the contact alias list: - $concattedContacts = "ARRAY_TO_STRING(ARRAY_AGG(DISTINCT cno.name1), ', ')"; - break; - case 'oracle': - // TODO: This is only valid for Oracle >= 11g Release 2 - $concattedContacts = "LISTAGG(cno.name1, ', ') WITHIN GROUP (ORDER BY cno.name1)"; - // Alternatives: - // - // RTRIM(XMLAGG(XMLELEMENT(e, column_name, ',').EXTRACT('//text()')), - // - // not supported and not documented but works since 10.1, - // however it is NOT always present: - // WM_CONCAT(c.alias) + $concattedContacts = "ARRAY_TO_STRING(ARRAY_AGG(DISTINCT co.name1), ', ')"; break; } - $this->columnMap['history']['output'] = "('[' || $concattedContacts || '] ' || sn.output)"; $this->select->from( @@ -141,20 +112,8 @@ class ServicenotificationQuery extends IdoQuery array() ); $this->select->joinLeft( - array('cno' => $this->prefix . 'objects'), - 'cno.object_id = cn.contact_object_id', - array() - ); - } - - /** - * Join acknowledgements - */ - protected function joinAcknowledgements() - { - $this->select->joinLeft( - array('a' => $this->prefix . 'acknowledgements'), - 'a.object_id = sn.object_id', + array('co' => $this->prefix . 'objects'), + 'co.object_id = cn.contact_object_id AND co.is_active = 1 AND co.objecttype_id = 10', array() ); } @@ -243,6 +202,7 @@ class ServicenotificationQuery extends IdoQuery public function getGroup() { $group = array(); + if ( $this->hasJoinedVirtualTable('history') || $this->hasJoinedVirtualTable('hostgroups') @@ -250,10 +210,10 @@ class ServicenotificationQuery extends IdoQuery ) { $group = array('sn.notification_id', 'so.object_id'); if ($this->hasJoinedVirtualTable('contactnotifications') && !$this->hasJoinedVirtualTable('history')) { - $group[] = 'cno.object_id'; + $group[] = 'co.object_id'; } } elseif ($this->hasJoinedVirtualTable('contactnotifications')) { - $group = array('sn.notification_id', 'cno.object_id', 'so.object_id'); + $group = array('sn.notification_id', 'co.object_id', 'so.object_id'); } if (! empty($group)) { @@ -265,10 +225,6 @@ class ServicenotificationQuery extends IdoQuery $group[] = 's.service_id'; } - if ($this->hasJoinedVirtualTable('acknowledgements')) { - $group[] = 'a.acknowledgement_id'; - } - if ($this->hasJoinedVirtualTable('instances')) { $group[] = 'i.instance_id'; } diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicestatusQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicestatusQuery.php index 99c37613d..3aadb1047 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicestatusQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicestatusQuery.php @@ -131,11 +131,6 @@ class ServicestatusQuery extends IdoQuery ELSE 4 END END - END - + - CASE WHEN hs.state_type = 1 - THEN 8 - ELSE 0 END', 'host_state' => 'CASE WHEN hs.has_been_checked = 0 OR hs.has_been_checked IS NULL THEN 99 ELSE hs.current_state END', 'host_state_type' => 'hs.state_type', @@ -271,11 +266,6 @@ class ServicestatusQuery extends IdoQuery END END END - END - + - CASE WHEN ss.state_type = 1 - THEN 8 - ELSE 0 END', 'service_state' => 'CASE WHEN ss.has_been_checked = 0 OR ss.has_been_checked IS NULL THEN 99 ELSE ss.current_state END', 'service_state_type' => 'ss.state_type', diff --git a/modules/monitoring/library/Monitoring/Command/IcingaApiCommand.php b/modules/monitoring/library/Monitoring/Command/IcingaApiCommand.php new file mode 100644 index 000000000..c33157f65 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/IcingaApiCommand.php @@ -0,0 +1,126 @@ +setEndpoint($endpoint) + ->setData($data); + return $command; + } + + /** + * Get the command data + * + * @return array + */ + public function getData() + { + return $this->data; + } + + /** + * Set the command data + * + * @param array $data + * + * @return $this + */ + public function setData($data) + { + $this->data = $data; + + return $this; + } + + /** + * Get the name of the endpoint + * + * @return string + */ + public function getEndpoint() + { + return $this->endpoint; + } + + /** + * Set the name of the endpoint + * + * @param string $endpoint + * + * @return $this + */ + public function setEndpoint($endpoint) + { + $this->endpoint = $endpoint; + + return $this; + } + + /** + * Get whether another Icinga API command should be sent after this one + * + * @return bool + */ + public function hasNext() + { + return $this->next !== null; + } + + /** + * Get the next Icinga API command + * + * @return IcingaApiCommand + */ + public function getNext() + { + return $this->next; + } + + /** + * Set the next Icinga API command + * + * @param IcingaApiCommand $next + * + * @return IcingaApiCommand + */ + public function setNext(IcingaApiCommand $next) + { + $this->next = $next; + return $next; + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Object/DeleteCommentCommand.php b/modules/monitoring/library/Monitoring/Command/Object/DeleteCommentCommand.php index 9ba9e0d85..4d791064f 100644 --- a/modules/monitoring/library/Monitoring/Command/Object/DeleteCommentCommand.php +++ b/modules/monitoring/library/Monitoring/Command/Object/DeleteCommentCommand.php @@ -17,6 +17,15 @@ class DeleteCommentCommand extends IcingaCommand */ protected $commentId; + /** + * Name of the comment (Icinga 2.4+) + * + * Required for removing the comment via Icinga 2's API. + * + * @var string + */ + protected $commentName; + /** * Whether the command affects a service comment * @@ -47,6 +56,33 @@ class DeleteCommentCommand extends IcingaCommand return $this; } + /** + * Get the name of the comment (Icinga 2.4+) + * + * Required for removing the comment via Icinga 2's API. + * + * @return string + */ + public function getCommentName() + { + return $this->commentName; + } + + /** + * Set the name of the comment (Icinga 2.4+) + * + * Required for removing the comment via Icinga 2's API. + * + * @param string $commentName + * + * @return $this + */ + public function setCommentName($commentName) + { + $this->commentName = $commentName; + return $this; + } + /** * Get whether the command affects a service comment * diff --git a/modules/monitoring/library/Monitoring/Command/Object/DeleteDowntimeCommand.php b/modules/monitoring/library/Monitoring/Command/Object/DeleteDowntimeCommand.php index ded5cc5e2..63f4bbce3 100644 --- a/modules/monitoring/library/Monitoring/Command/Object/DeleteDowntimeCommand.php +++ b/modules/monitoring/library/Monitoring/Command/Object/DeleteDowntimeCommand.php @@ -17,6 +17,15 @@ class DeleteDowntimeCommand extends IcingaCommand */ protected $downtimeId; + /** + * Name of the downtime (Icinga 2.4+) + * + * Required for removing the downtime via Icinga 2's API. + * + * @var string + */ + protected $downtimeName; + /** * Whether the command affects a service downtime * @@ -47,6 +56,33 @@ class DeleteDowntimeCommand extends IcingaCommand return $this; } + /** + * Get the name of the downtime (Icinga 2.4+) + * + * Required for removing the downtime via Icinga 2's API. + * + * @return string + */ + public function getDowntimeName() + { + return $this->downtimeName; + } + + /** + * Set the name of the downtime (Icinga 2.4+) + * + * Required for removing the downtime via Icinga 2's API. + * + * @param string $downtimeName + * + * @return $this + */ + public function setDowntimeName($downtimeName) + { + $this->downtimeName = $downtimeName; + return $this; + } + /** * Get whether the command affects a service * diff --git a/modules/monitoring/library/Monitoring/Command/Renderer/IcingaApiCommandRenderer.php b/modules/monitoring/library/Monitoring/Command/Renderer/IcingaApiCommandRenderer.php new file mode 100644 index 000000000..30dc414dc --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Renderer/IcingaApiCommandRenderer.php @@ -0,0 +1,297 @@ +app; + } + + /** + * Set the name of the Icinga application object + * + * @param string $app + * + * @return $this + */ + public function setApp($app) + { + $this->app = $app; + + return $this; + } + + /** + * Apply filter to query data + * + * @param array $data + * @param MonitoredObject $object + * + * @return array + */ + protected function applyFilter(array &$data, MonitoredObject $object) + { + if ($object->getType() === $object::TYPE_HOST) { + /** @var \Icinga\Module\Monitoring\Object\Host $object */ + $data['host'] = $object->getName(); + } else { + /** @var \Icinga\Module\Monitoring\Object\Service $object */ + $data['service'] = sprintf('%s!%s', $object->getHost()->getName(), $object->getName()); + } + } + + /** + * Render a command + * + * @param IcingaCommand $command + * + * @return IcingaApiCommand + */ + public function render(IcingaCommand $command) + { + $renderMethod = 'render' . $command->getName(); + if (! method_exists($this, $renderMethod)) { + die($renderMethod); + } + return $this->$renderMethod($command); + } + + public function renderAddComment(AddCommentCommand $command) + { + $endpoint = 'actions/add-comment'; + $data = array( + 'author' => $command->getAuthor(), + 'comment' => $command->getComment() + ); + $this->applyFilter($data, $command->getObject()); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderSendCustomNotification(SendCustomNotificationCommand $command) + { + $endpoint = 'actions/send-custom-notification'; + $data = array( + 'author' => $command->getAuthor(), + 'comment' => $command->getComment(), + 'force' => $command->getForced() + ); + $this->applyFilter($data, $command->getObject()); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderProcessCheckResult(ProcessCheckResultCommand $command) + { + $endpoint = 'actions/process-check-result'; + $data = array( + 'exit_status' => $command->getStatus(), + 'plugin_output' => $command->getOutput(), + 'performance_data' => $command->getPerformanceData() + ); + $this->applyFilter($data, $command->getObject()); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderScheduleCheck(ScheduleServiceCheckCommand $command) + { + $endpoint = 'actions/reschedule-check'; + $data = array( + 'next_check' => $command->getCheckTime(), + 'force_check' => $command->getForced() + ); + $this->applyFilter($data, $command->getObject()); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderScheduleDowntime(ScheduleServiceDowntimeCommand $command) + { + $endpoint = 'actions/schedule-downtime'; + $data = array( + 'author' => $command->getAuthor(), + 'comment' => $command->getComment(), + 'start_time' => $command->getStart(), + 'end_time' => $command->getEnd(), + 'duration' => $command->getDuration(), + 'fixed' => $command->getFixed(), + 'trigger_name' => $command->getTriggerId() + ); + $commandData = $data; + if ($command instanceof PropagateHostDowntimeCommand) { + /** @var \Icinga\Module\Monitoring\Command\Object\PropagateHostDowntimeCommand $command */ + $commandData['child_options'] = $command->getTriggered() ? 1 : 2; + } + $this->applyFilter($commandData, $command->getObject()); + $apiCommand = IcingaApiCommand::create($endpoint, $commandData); + if ($command instanceof ScheduleHostDowntimeCommand + /** @var \Icinga\Module\Monitoring\Command\Object\ScheduleHostDowntimeCommand $command */ + && $command->getForAllServices() + ) { + $commandData = $data + array( + 'type' => 'Service', + 'filter' => 'host.name == host_name', + 'filter_vars' => array( + 'host_name' => $command->getObject()->getName() + ) + ); + $apiCommand->setNext(IcingaApiCommand::create($endpoint, $commandData)); + } + return $apiCommand; + } + + public function renderAcknowledgeProblem(AcknowledgeProblemCommand $command) + { + $endpoint = 'actions/acknowledge-problem'; + $data = array( + 'author' => $command->getAuthor(), + 'comment' => $command->getComment(), + 'expiry' => $command->getExpireTime(), + 'sticky' => $command->getSticky(), + 'notify' => $command->getNotify() + ); + $this->applyFilter($data, $command->getObject()); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderToggleObjectFeature(ToggleObjectFeatureCommand $command) + { + if ($command->getEnabled() === true) { + $enabled = true; + } else { + $enabled = false; + } + switch ($command->getFeature()) { + case ToggleObjectFeatureCommand::FEATURE_ACTIVE_CHECKS: + $attr = 'enable_active_checks'; + break; + case ToggleObjectFeatureCommand::FEATURE_PASSIVE_CHECKS: + $attr = 'enable_passive_checks'; + break; + case ToggleObjectFeatureCommand::FEATURE_NOTIFICATIONS: + $attr = 'enable_notifications'; + break; + case ToggleObjectFeatureCommand::FEATURE_EVENT_HANDLER: + $attr = 'enable_event_handler'; + break; + case ToggleObjectFeatureCommand::FEATURE_FLAP_DETECTION: + $attr = 'enable_flapping'; + break; + default: + throw new InvalidArgumentException($command->getFeature()); + } + $endpoint = 'objects/'; + $object = $command->getObject(); + if ($object->getType() === ToggleObjectFeatureCommand::TYPE_HOST) { + /** @var \Icinga\Module\Monitoring\Object\Host $object */ + $endpoint .= 'hosts'; + } else { + /** @var \Icinga\Module\Monitoring\Object\Service $object */ + $endpoint .= 'services'; + } + $data = array( + 'attrs' => array( + $attr => $enabled + ) + ); + $this->applyFilter($data, $object); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderDeleteComment(DeleteCommentCommand $command) + { + $endpoint = 'actions/remove-comment'; + $data = array( + 'comment' => $command->getCommentName() + ); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderDeleteDowntime(DeleteDowntimeCommand $command) + { + $endpoint = 'actions/remove-downtime'; + $data = array( + 'downtime' => $command->getDowntimeName() + ); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderRemoveAcknowledgement(RemoveAcknowledgementCommand $command) + { + $endpoint = 'actions/remove-acknowledgement'; + $data = array(); + $this->applyFilter($data, $command->getObject()); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderToggleInstanceFeature(ToggleInstanceFeatureCommand $command) + { + $endpoint = 'objects/icingaapplications/' . $this->getApp(); + if ($command->getEnabled() === true) { + $enabled = true; + } else { + $enabled = false; + } + switch ($command->getFeature()) { + case ToggleInstanceFeatureCommand::FEATURE_ACTIVE_HOST_CHECKS: + $attr = 'enable_host_checks'; + break; + case ToggleInstanceFeatureCommand::FEATURE_ACTIVE_SERVICE_CHECKS: + $attr = 'enable_service_checks'; + break; + case ToggleInstanceFeatureCommand::FEATURE_EVENT_HANDLERS: + $attr = 'enable_event_handlers'; + break; + case ToggleInstanceFeatureCommand::FEATURE_FLAP_DETECTION: + $attr = 'enable_flapping'; + break; + case ToggleInstanceFeatureCommand::FEATURE_NOTIFICATIONS: + $attr = 'enable_notifications'; + break; + case ToggleInstanceFeatureCommand::FEATURE_PERFORMANCE_DATA: + $attr = 'enable_perfdata'; + break; + default: + throw new InvalidArgumentException($command->getFeature()); + } + $data = array( + 'attrs' => array( + $attr => $enabled + ) + ); + return IcingaApiCommand::create($endpoint, $data); + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php b/modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php new file mode 100644 index 000000000..8b00bd1b6 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php @@ -0,0 +1,234 @@ +renderer = new IcingaApiCommandRenderer(); + } + + /** + * Set the name of the Icinga application object + * + * @param string $app + * + * @return $this + */ + public function setApp($app) + { + $this->renderer->setApp($app); + + return $this; + } + + /** + * Get the API host + * + * @return string + */ + public function getHost() + { + return $this->host; + } + + /** + * Set the API host + * + * @param string $host + * + * @return $this + */ + public function setHost($host) + { + $this->host = $host; + + return $this; + } + + /** + * Get the API password + * + * @return string + */ + public function getPassword() + { + return $this->password; + } + + /** + * Set the API password + * + * @param string $password + * + * @return $this + */ + public function setPassword($password) + { + $this->password = $password; + + return $this; + } + + /** + * Get the API port + * + * @return int + */ + public function getPort() + { + return $this->port; + } + + /** + * Set the API port + * + * @param int $port + * + * @return $this + */ + public function setPort($port) + { + $this->port = (int) $port; + + return $this; + } + + /** + * Get the API username + * + * @return string + */ + public function getUsername() + { + return $this->username; + } + + /** + * Set the API username + * + * @param string $username + * + * @return $this + */ + public function setUsername($username) + { + $this->username = $username; + + return $this; + } + + /** + * Get URI for endpoint + * + * @param string $endpoint + * + * @return string + */ + protected function getUriFor($endpoint) + { + return sprintf('https://%s:%u/v1/%s', $this->getHost(), $this->getPort(), $endpoint); + } + + protected function sendCommand(IcingaApiCommand $command) + { + Logger::debug( + 'Sending Icinga command "%s" to the API "%s:%u"', + $command->getEndpoint(), + $this->getHost(), + $this->getPort() + ); + $response = RestRequest::post($this->getUriFor($command->getEndpoint())) + ->authenticateWith($this->getUsername(), $this->getPassword()) + ->sendJson() + ->noStrictSsl() + ->setPayload($command->getData()) + ->send(); + if (isset($response['error'])) { + throw new CommandTransportException( + 'Can\'t send external Icinga command: %u %s', + $response['error'], + $response['status'] + ); + } + $result = array_pop($response['results']); + if ($result['code'] < 200 || $result['code'] >= 300) { + throw new CommandTransportException( + 'Can\'t send external Icinga command: %u %s', + $result['code'], + $result['status'] + ); + } + if ($command->hasNext()) { + $this->sendCommand($command->getNext()); + } + } + + /** + * Send the Icinga command over the Icinga 2 API + * + * @param IcingaCommand $command + * @param int|null $now + * + * @throws CommandTransportException + */ + public function send(IcingaCommand $command, $now = null) + { + $this->sendCommand($this->renderer->render($command)); + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Transport/CommandTransport.php b/modules/monitoring/library/Monitoring/Command/Transport/CommandTransport.php index 81fb54939..512de2f28 100644 --- a/modules/monitoring/library/Monitoring/Command/Transport/CommandTransport.php +++ b/modules/monitoring/library/Monitoring/Command/Transport/CommandTransport.php @@ -65,6 +65,9 @@ class CommandTransport implements CommandTransportInterface case RemoteCommandFile::TRANSPORT: $transport = new RemoteCommandFile(); break; + case ApiCommandTransport::TRANSPORT: + $transport = new ApiCommandTransport(); + break; case LocalCommandFile::TRANSPORT: case '': // Casting null to string is the empty string $transport = new LocalCommandFile(); @@ -74,12 +77,13 @@ class CommandTransport implements CommandTransportInterface mt( 'monitoring', 'Cannot create command transport "%s". Invalid transport' - . ' defined in "%s". Use one of "%s" or "%s".' + . ' defined in "%s". Use one of "%s", "%s" or "%s".' ), $config->transport, static::getConfig()->getConfigFile(), LocalCommandFile::TRANSPORT, - RemoteCommandFile::TRANSPORT + RemoteCommandFile::TRANSPORT, + ApiCommandTransport::TRANSPORT ); } diff --git a/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php b/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php index 4ac365001..5426bb90a 100644 --- a/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php +++ b/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php @@ -73,6 +73,20 @@ class RemoteCommandFile implements CommandTransportInterface */ protected $renderer; + /** + * SSH subprocess pipes + * + * @var array + */ + protected $sshPipes; + + /** + * SSH subprocess + * + * @var resource + */ + protected $sshProcess; + /** * Create a new remote command file command transport */ @@ -274,26 +288,178 @@ class RemoteCommandFile implements CommandTransportInterface $this->port, $this->path ); - $ssh = sprintf('ssh -o BatchMode=yes -p %u', $this->port); + return $this->sendCommandString($commandString); + } + + /** + * Get the SSH command + * + * @return string + */ + protected function sshCommand() + { + $cmd = sprintf( + 'exec ssh -o BatchMode=yes -p %u', + $this->port + ); // -o BatchMode=yes for disabling interactive authentication methods + if (isset($this->user)) { - $ssh .= sprintf(' -l %s', escapeshellarg($this->user)); + $cmd .= ' -l ' . escapeshellarg($this->user); } + if (isset($this->privateKey)) { - $ssh .= sprintf(' -o StrictHostKeyChecking=no -i %s', escapeshellarg($this->privateKey)); + // TODO: StrictHostKeyChecking=no for compat only, must be removed + $cmd .= ' -o StrictHostKeyChecking=no' + . ' -i ' . escapeshellarg($this->privateKey); } - $ssh .= sprintf( - ' %s "echo %s > %s" 2>&1', // Redirect stderr to stdout + + $cmd .= sprintf( + ' %s "cat > %s"', escapeshellarg($this->host), - escapeshellarg($commandString), escapeshellarg($this->path) ); - exec($ssh, $output, $status); - if ($status !== 0) { + + return $cmd; + } + + /** + * Send the command over SSH + * + * @param string $commandString + * + * @throws CommandTransportException + */ + protected function sendCommandString($commandString) + { + if ($this->isSshAlive()) { + $ret = fwrite($this->sshPipes[0], $commandString . "\n"); + if ($ret === false) { + $this->throwSshFailure('Cannot write to the remote command pipe'); + } elseif ($ret !== strlen($commandString) + 1) { + $this->throwSshFailure( + 'Failed to write the whole command to the remote command pipe' + ); + } + } else { + $this->throwSshFailure(); + } + } + + /** + * Get the pipes of the SSH subprocess + * + * @return array + */ + protected function getSshPipes() + { + if ($this->sshPipes === null) { + $this->forkSsh(); + } + + return $this->sshPipes; + } + + /** + * Get the SSH subprocess + * + * @return resource + */ + protected function getSshProcess() + { + if ($this->sshProcess === null) { + $this->forkSsh(); + } + + return $this->sshProcess; + } + + /** + * Get the status of the SSH subprocess + * + * @param string $what + * + * @return mixed + */ + protected function getSshProcessStatus($what = null) + { + $status = proc_get_status($this->getSshProcess()); + if ($what === null) { + return $status; + } else { + return $status[$what]; + } + } + + /** + * Get whether the SSH subprocess is alive + * + * @return bool + */ + protected function isSshAlive() + { + return $this->getSshProcessStatus('running'); + } + + /** + * Fork SSH subprocess + * + * @throws CommandTransportException If fork fails + */ + protected function forkSsh() + { + $descriptors = array( + 0 => array('pipe', 'r'), + 1 => array('pipe', 'w'), + 2 => array('pipe', 'w') + ); + + $this->sshProcess = proc_open($this->sshCommand(), $descriptors, $this->sshPipes); + + if (! is_resource($this->sshProcess)) { throw new CommandTransportException( - 'Can\'t send external Icinga command: %s', - implode(' ', $output) + 'Can\'t send external Icinga command: Failed to fork SSH' ); } } + + /** + * Read from STDERR + * + * @return string + */ + protected function readStderr() + { + return stream_get_contents($this->sshPipes[2]); + } + + /** + * Throw SSH failure + * + * @param string $msg + * + * @throws CommandTransportException + */ + protected function throwSshFailure($msg = 'Can\'t send external Icinga command') + { + throw new CommandTransportException( + '%s: %s', + $msg, + $this->readStderr() . var_export($this->getSshProcessStatus(), true) + ); + } + + /** + * Close SSH pipes and SSH subprocess + */ + public function __destruct() + { + if (is_resource($this->sshProcess)) { + fclose($this->sshPipes[0]); + fclose($this->sshPipes[1]); + fclose($this->sshPipes[2]); + + proc_close($this->sshProcess); + } + } } diff --git a/modules/monitoring/library/Monitoring/DataView/Comment.php b/modules/monitoring/library/Monitoring/DataView/Comment.php index 56facbec6..3a035bc79 100644 --- a/modules/monitoring/library/Monitoring/DataView/Comment.php +++ b/modules/monitoring/library/Monitoring/DataView/Comment.php @@ -19,6 +19,7 @@ class Comment extends DataView 'comment_expiration', 'comment_internal_id', 'comment_is_persistent', + 'comment_name', 'comment_timestamp', 'comment_type', 'host_display_name', diff --git a/modules/monitoring/library/Monitoring/DataView/Downtime.php b/modules/monitoring/library/Monitoring/DataView/Downtime.php index 8e14a13ee..ca42e2da5 100644 --- a/modules/monitoring/library/Monitoring/DataView/Downtime.php +++ b/modules/monitoring/library/Monitoring/DataView/Downtime.php @@ -23,6 +23,7 @@ class Downtime extends DataView 'downtime_is_fixed', 'downtime_is_flexible', 'downtime_is_in_effect', + 'downtime_name', 'downtime_scheduled_end', 'downtime_scheduled_start', 'downtime_start', diff --git a/modules/monitoring/library/Monitoring/DataView/Hostcomment.php b/modules/monitoring/library/Monitoring/DataView/Hostcomment.php index 92020abe5..74fc2efef 100644 --- a/modules/monitoring/library/Monitoring/DataView/Hostcomment.php +++ b/modules/monitoring/library/Monitoring/DataView/Hostcomment.php @@ -20,6 +20,7 @@ class Hostcomment extends DataView 'comment_expiration', 'comment_internal_id', 'comment_is_persistent', + 'comment_name', 'comment_timestamp', 'comment_type', 'host_display_name', diff --git a/modules/monitoring/library/Monitoring/DataView/Hostdowntime.php b/modules/monitoring/library/Monitoring/DataView/Hostdowntime.php index 8782b33fe..f5e4e8005 100644 --- a/modules/monitoring/library/Monitoring/DataView/Hostdowntime.php +++ b/modules/monitoring/library/Monitoring/DataView/Hostdowntime.php @@ -24,6 +24,7 @@ class Hostdowntime extends DataView 'downtime_is_fixed', 'downtime_is_flexible', 'downtime_is_in_effect', + 'downtime_name', 'downtime_scheduled_end', 'downtime_scheduled_start', 'downtime_start', diff --git a/modules/monitoring/library/Monitoring/DataView/Notification.php b/modules/monitoring/library/Monitoring/DataView/Notification.php index febf9e343..b9a85cd2c 100644 --- a/modules/monitoring/library/Monitoring/DataView/Notification.php +++ b/modules/monitoring/library/Monitoring/DataView/Notification.php @@ -11,18 +11,12 @@ class Notification extends DataView public function getColumns() { return array( - 'instance_name', - 'notification_state', - 'notification_start_time', - 'notification_contact_name', - 'notification_output', - 'notification_object_id', - 'contact_object_id', - 'acknowledgement_entry_time', - 'acknowledgement_author_name', - 'acknowledgement_comment_data', 'host_display_name', 'host_name', + 'notification_contact_name', + 'notification_output', + 'notification_state', + 'notification_timestamp', 'object_type', 'service_description', 'service_display_name', @@ -36,23 +30,8 @@ class Notification extends DataView public function getSortRules() { return array( - 'notification_start_time' => array( - 'order' => self::SORT_DESC, - 'title' => 'Notification Start' - ), - 'host_display_name' => array( - 'columns' => array( - 'host_display_name', - 'service_display_name' - ), - 'order' => self::SORT_ASC - ), - 'service_display_name' => array( - 'columns' => array( - 'service_display_name', - 'host_display_name' - ), - 'order' => self::SORT_ASC + 'notification_timestamp' => array( + 'order' => self::SORT_DESC ) ); } @@ -63,11 +42,9 @@ class Notification extends DataView public function getStaticFilterColumns() { return array( - 'contact', - 'host', 'host_alias', - 'hostgroup', 'hostgroup_alias', 'hostgroup_name', - 'service', - 'servicegroup', 'servicegroup_alias', 'servicegroup_name' + 'hostgroup_name', + 'instance_name', + 'servicegroup_name' ); } diff --git a/modules/monitoring/library/Monitoring/DataView/Servicecomment.php b/modules/monitoring/library/Monitoring/DataView/Servicecomment.php index b404955d7..78c133386 100644 --- a/modules/monitoring/library/Monitoring/DataView/Servicecomment.php +++ b/modules/monitoring/library/Monitoring/DataView/Servicecomment.php @@ -20,6 +20,7 @@ class Servicecomment extends DataView 'comment_expiration', 'comment_internal_id', 'comment_is_persistent', + 'comment_name', 'comment_timestamp', 'comment_type', 'host_display_name', diff --git a/modules/monitoring/library/Monitoring/DataView/Servicedowntime.php b/modules/monitoring/library/Monitoring/DataView/Servicedowntime.php index baed8c195..43d895ede 100644 --- a/modules/monitoring/library/Monitoring/DataView/Servicedowntime.php +++ b/modules/monitoring/library/Monitoring/DataView/Servicedowntime.php @@ -21,6 +21,7 @@ class Servicedowntime extends DataView 'downtime_is_fixed', 'downtime_is_flexible', 'downtime_is_in_effect', + 'downtime_name', 'downtime_scheduled_end', 'downtime_scheduled_start', 'downtime_start', diff --git a/modules/monitoring/library/Monitoring/Object/Host.php b/modules/monitoring/library/Monitoring/Object/Host.php index 357c775e9..7c6bd5324 100644 --- a/modules/monitoring/library/Monitoring/Object/Host.php +++ b/modules/monitoring/library/Monitoring/Object/Host.php @@ -202,9 +202,4 @@ class Host extends MonitoredObject MonitoredObject::parseAttributeUrls($this->host_notes_url) ); } - - public function getNotes() - { - return $this->host_notes; - } } diff --git a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php index 437268eb8..421caaa3e 100644 --- a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php +++ b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php @@ -173,13 +173,6 @@ abstract class MonitoredObject implements Filterable */ abstract protected function getDataView(); - /** - * Get the notes for this monitored object - * - * @return string The notes as a string - */ - public abstract function getNotes(); - /** * Get all note urls configured for this monitored object * @@ -332,6 +325,7 @@ abstract class MonitoredObject implements Filterable 'comment' => 'comment_data', 'expiration' => 'comment_expiration', 'id' => 'comment_internal_id', + 'name' => 'comment_name', 'persistent' => 'comment_is_persistent', 'timestamp' => 'comment_timestamp', 'type' => 'comment_type' @@ -445,7 +439,7 @@ abstract class MonitoredObject implements Filterable } $blacklist = array(); - $blacklistPattern = '/^(.*pw.*|.*pass.*|community)$/i'; + $blacklistPattern = ''; if (($blacklistConfig = Config::module('monitoring')->get('security', 'protected_customvars', '')) !== '') { foreach (explode(',', $blacklistConfig) as $customvar) { @@ -468,7 +462,10 @@ abstract class MonitoredObject implements Filterable $this->customvars = $customvars; $this->hideBlacklistedProperties(); - $this->customvars = $this->obfuscateCustomVars($this->customvars, $blacklistPattern); + + if ($blacklistPattern) { + $this->customvars = $this->obfuscateCustomVars($this->customvars, $blacklistPattern); + } return $this; } @@ -584,19 +581,20 @@ abstract class MonitoredObject implements Filterable public function fetchDowntimes() { $downtimes = $this->backend->select()->from('downtime', array( - 'id' => 'downtime_internal_id', - 'objecttype' => 'object_type', - 'comment' => 'downtime_comment', 'author_name' => 'downtime_author_name', - 'start' => 'downtime_start', - 'scheduled_start' => 'downtime_scheduled_start', - 'scheduled_end' => 'downtime_scheduled_end', - 'end' => 'downtime_end', + 'comment' => 'downtime_comment', 'duration' => 'downtime_duration', - 'is_flexible' => 'downtime_is_flexible', + 'end' => 'downtime_end', + 'entry_time' => 'downtime_entry_time', + 'id' => 'downtime_internal_id', 'is_fixed' => 'downtime_is_fixed', + 'is_flexible' => 'downtime_is_flexible', 'is_in_effect' => 'downtime_is_in_effect', - 'entry_time' => 'downtime_entry_time' + 'name' => 'downtime_name', + 'objecttype' => 'object_type', + 'scheduled_end' => 'downtime_scheduled_end', + 'scheduled_start' => 'downtime_scheduled_start', + 'start' => 'downtime_start' )) ->where('object_type', $this->type) ->order('downtime_is_in_effect', 'DESC') diff --git a/modules/monitoring/library/Monitoring/Object/Service.php b/modules/monitoring/library/Monitoring/Object/Service.php index 9e60898ac..c712beeb1 100644 --- a/modules/monitoring/library/Monitoring/Object/Service.php +++ b/modules/monitoring/library/Monitoring/Object/Service.php @@ -118,6 +118,7 @@ class Service extends MonitoredObject 'host_display_name', 'host_handled', 'host_in_downtime', + 'host_is_flapping', 'host_last_state_change', 'host_name', 'host_notifications_enabled', @@ -213,9 +214,4 @@ class Service extends MonitoredObject MonitoredObject::parseAttributeUrls($this->service_notes_url) ); } - - public function getNotes() - { - return $this->service_notes; - } } diff --git a/modules/monitoring/library/Monitoring/Plugin/Perfdata.php b/modules/monitoring/library/Monitoring/Plugin/Perfdata.php index 0c9f09d90..9008a27ab 100644 --- a/modules/monitoring/library/Monitoring/Plugin/Perfdata.php +++ b/modules/monitoring/library/Monitoring/Plugin/Perfdata.php @@ -282,7 +282,7 @@ class Perfdata $parts = explode(';', $this->perfdataValue); $matches = array(); - if (preg_match('@^(\d+(\.\d+)?)([a-zA-Z%]{1,2})$@', $parts[0], $matches)) { + if (preg_match('@^(-?\d+(\.\d+)?)([a-zA-Z%]{1,2})$@', $parts[0], $matches)) { $this->unit = strtolower($matches[3]); $this->value = self::convert($matches[1], $this->unit); } else { diff --git a/modules/monitoring/library/Monitoring/Timeline/TimeEntry.php b/modules/monitoring/library/Monitoring/Timeline/TimeEntry.php index 4c721b808..ee313b3c8 100644 --- a/modules/monitoring/library/Monitoring/Timeline/TimeEntry.php +++ b/modules/monitoring/library/Monitoring/Timeline/TimeEntry.php @@ -55,11 +55,11 @@ class TimeEntry protected $label; /** - * The color of this group + * The CSS class of the entry * * @var string */ - protected $color; + protected $class; /** * Return a new TimeEntry object with the given attributes being set @@ -209,22 +209,25 @@ class TimeEntry } /** - * Set this group's color - * - * @param string $color The color to set. (The css name or hex-code) - */ - public function setColor($color) - { - $this->color = $color; - } - - /** - * Get the color of this group + * Get the CSS class * * @return string */ - public function getColor() + public function getClass() { - return $this->color; + return $this->class; + } + + /** + * Set the CSS class + * + * @param string $class + * + * @return $this + */ + public function setClass($class) + { + $this->class = $class; + return $this; } } diff --git a/modules/monitoring/library/Monitoring/Timeline/TimeLine.php b/modules/monitoring/library/Monitoring/Timeline/TimeLine.php index 08e5b7a48..5ed48a43c 100644 --- a/modules/monitoring/library/Monitoring/Timeline/TimeLine.php +++ b/modules/monitoring/library/Monitoring/Timeline/TimeLine.php @@ -200,7 +200,7 @@ class TimeLine implements IteratorAggregate } /** - * Return all known group types (identifiers) with their respective labels and colors as array + * Return all known group types (identifiers) with their respective labels and classess as array * * @return array */ @@ -208,8 +208,8 @@ class TimeLine implements IteratorAggregate { $groupInfo = array(); foreach ($this->identifiers as $name => $attributes) { + $groupInfo[$name]['class'] = $attributes['class']; $groupInfo[$name]['label'] = $attributes['label']; - $groupInfo[$name]['color'] = $attributes['color']; } return $groupInfo; diff --git a/modules/monitoring/library/Monitoring/TransportStep.php b/modules/monitoring/library/Monitoring/TransportStep.php index 76f2d18e9..d138eb407 100644 --- a/modules/monitoring/library/Monitoring/TransportStep.php +++ b/modules/monitoring/library/Monitoring/TransportStep.php @@ -4,6 +4,7 @@ namespace Icinga\Module\Monitoring; use Exception; +use Icinga\Exception\ProgrammingError; use Icinga\Module\Setup\Step; use Icinga\Application\Config; use Icinga\Exception\IcingaException; @@ -40,47 +41,83 @@ class TransportStep extends Step public function getSummary() { - $pageTitle = '

' . mt('monitoring', 'Command Transport', 'setup.page.title') . '

'; - - if (isset($this->data['transportConfig']['host'])) { - $pipeHtml = '

' . sprintf( - mt( - 'monitoring', - 'Icinga Web 2 will use the named pipe located on a remote machine at "%s" to send commands' - . ' to your monitoring instance by using the connection details listed below:' - ), - $this->data['transportConfig']['path'] - ) . '

'; - - $pipeHtml .= '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '
' . mt('monitoring', 'Remote Host') . '' . $this->data['transportConfig']['host'] . '
' . mt('monitoring', 'Remote SSH Port') . '' . $this->data['transportConfig']['port'] . '
' . mt('monitoring', 'Remote SSH User') . '' . $this->data['transportConfig']['user'] . '
'; - } else { - $pipeHtml = '

' . sprintf( - mt( - 'monitoring', - 'Icinga Web 2 will use the named pipe located at "%s"' - . ' to send commands to your monitoring instance.' - ), - $this->data['transportConfig']['path'] - ) . '

'; + switch ($this->data['transportConfig']['transport']) { + case 'local': + $details = '

' . sprintf( + mt( + 'monitoring', + 'Icinga Web 2 will use the named pipe located at "%s"' + . ' to send commands to your monitoring instance.' + ), + $this->data['transportConfig']['path'] + ) . '

'; + break; + case 'remote': + $details = '

' + . sprintf( + mt( + 'monitoring', + 'Icinga Web 2 will use the named pipe located on a remote machine at "%s" to send commands' + . ' to your monitoring instance by using the connection details listed below:' + ), + $this->data['transportConfig']['path'] + ) + . '

' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '
' . mt('monitoring', 'Remote Host') . '' . $this->data['transportConfig']['host'] . '
' . mt('monitoring', 'Remote SSH Port') . '' . $this->data['transportConfig']['port'] . '
' . mt('monitoring', 'Remote SSH User') . '' . $this->data['transportConfig']['user'] . '
'; + break; + case 'api': + $details = '

' + . mt( + 'monitoring', + 'Icinga Web 2 will use the Icinga 2 API to send commands' + . ' to your monitoring instance by using the connection details listed below:' + ) + . '

' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '
' . mt('monitoring', 'Host') . '' . $this->data['transportConfig']['host'] . '
' . mt('monitoring', 'Port') . '' . $this->data['transportConfig']['port'] . '
' . mt('monitoring', 'Username') . '' . $this->data['transportConfig']['username'] . '
' . mt('monitoring', 'Password') . '' . str_repeat('*', strlen($this->data['transportConfig']['password'])) . '
'; + break; + default: + throw new ProgrammingError( + 'Unknown command transport type: %s', + $this->data['transportConfig']['transport'] + ); } - return $pageTitle . '
' . $pipeHtml . '
'; + return '

' . mt('monitoring', 'Command Transport', 'setup.page.title') . '

' + . '
' . $details . '
'; } public function getReport() diff --git a/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php b/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php index f7a0b8179..9ea05f02b 100644 --- a/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php +++ b/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php @@ -75,6 +75,7 @@ abstract class MonitoredObjectController extends Controller } } $this->object->populate(); + $this->handleFormatRequest(); $toggleFeaturesForm = new ToggleObjectFeaturesCommandForm(array( 'backend' => $this->backend, 'objects' => $this->object @@ -129,10 +130,31 @@ abstract class MonitoredObjectController extends Controller $this->view->form = $form; $this->view->object = $this->object; $this->view->tabs->remove('dashboard'); + $this->view->tabs->remove('menu-entry'); $this->_helper->viewRenderer('partials/command/object-command-form', null, true); return $form; } + /** + * Export to JSON if requested + */ + protected function handleFormatRequest($query = null) + { + if ($this->params->get('format') === 'json' + || $this->getRequest()->getHeader('Accept') === 'application/json' + ) { + $payload = (array) $this->object->properties; + $payload += array( + 'contacts' => $this->object->contacts->fetchPairs(), + 'contact_groups' => $this->object->contactgroups->fetchPairs(), + 'vars' => $this->object->customvars + ); + $groupName = $this->object->getType() . 'groups'; + $payload[$groupName] = $this->object->$groupName; + $this->getResponse()->json()->setSuccessData($payload)->sendResponse(); + } + } + /** * Acknowledge a problem */ diff --git a/modules/monitoring/library/Monitoring/Web/Navigation/Renderer/BackendAvailabilityNavigationItemRenderer.php b/modules/monitoring/library/Monitoring/Web/Navigation/Renderer/BackendAvailabilityNavigationItemRenderer.php index c269d328a..2e1d2ccd4 100644 --- a/modules/monitoring/library/Monitoring/Web/Navigation/Renderer/BackendAvailabilityNavigationItemRenderer.php +++ b/modules/monitoring/library/Monitoring/Web/Navigation/Renderer/BackendAvailabilityNavigationItemRenderer.php @@ -4,15 +4,22 @@ namespace Icinga\Module\Monitoring\Web\Navigation\Renderer; use Exception; -use Icinga\Web\Navigation\Renderer\BadgeNavigationItemRenderer; use Icinga\Module\Monitoring\Backend\MonitoringBackend; +use Icinga\Web\Navigation\Renderer\BadgeNavigationItemRenderer; class BackendAvailabilityNavigationItemRenderer extends BadgeNavigationItemRenderer { + /** + * Cached count + * + * @var int + */ + protected $count; + /** * Get whether or not the monitoring backend is currently running * - * @return bool + * @return object */ protected function isCurrentlyRunning() { @@ -20,51 +27,50 @@ class BackendAvailabilityNavigationItemRenderer extends BadgeNavigationItemRende ->select() ->from( 'programstatus', - array('is_currently_running') + array('is_currently_running', 'notifications_enabled') ) - ->fetchOne(); - return $programStatus !== false ? (bool) $programStatus : false; + ->fetchRow(); + return $programStatus; } /** - * The css class of the badge - * - * @return string - */ - public function getState() - { - return self::STATE_CRITICAL; - } - - /** - * The amount of items to display in the badge - * - * @return int + * {@inheritdoc} */ public function getCount() { - try { - if ($this->isCurrentlyRunning()) { - return 0; + if ($this->count === null) { + try { + $count = 0; + $programStatus = $this->isCurrentlyRunning(); + $titles = array(); + if (! (bool) $programStatus->notifications_enabled) { + $count = 1; + $this->state = static::STATE_WARNING; + $titles[] = mt('monitoring', 'Notifications are disabled'); + } + if (! (bool) $programStatus->is_currently_running) { + $count = 1; + $this->state = static::STATE_CRITICAL; + array_unshift($titles, sprintf( + mt('monitoring', 'Monitoring backend %s is not running'), + MonitoringBackend::instance()->getName() + )); + } + $this->count = $count; + $this->title = implode('. ', $titles); + } catch (Exception $_) { + $this->count = 1; } - } catch (Exception $_) { - // pass } - return 1; + return $this->count; } /** - * The tooltip title - * - * @return string - * @throws \Icinga\Exception\ConfigurationError + * {@inheritdoc} */ - public function getTitle() + public function getState() { - return sprintf( - mt('monitoring', 'Monitoring backend %s is not running'), - MonitoringBackend::instance()->getName() - ); + return $this->state; } } diff --git a/modules/monitoring/library/Monitoring/Web/Navigation/Renderer/MonitoringBadgeNavigationItemRenderer.php b/modules/monitoring/library/Monitoring/Web/Navigation/Renderer/MonitoringBadgeNavigationItemRenderer.php index 3f17a5e6a..f050ca410 100644 --- a/modules/monitoring/library/Monitoring/Web/Navigation/Renderer/MonitoringBadgeNavigationItemRenderer.php +++ b/modules/monitoring/library/Monitoring/Web/Navigation/Renderer/MonitoringBadgeNavigationItemRenderer.php @@ -22,6 +22,13 @@ use Icinga\Web\Navigation\Renderer\SummaryNavigationItemRenderer; */ class MonitoringBadgeNavigationItemRenderer extends SummaryNavigationItemRenderer { + /** + * Cached count + * + * @var int + */ + protected $count; + /** * Caches the responses for all executed summaries * @@ -164,20 +171,24 @@ class MonitoringBadgeNavigationItemRenderer extends SummaryNavigationItemRendere */ public function getCount() { - try { - $summary = self::summary($this->getDataView()); - } catch (Exception $_) { - return 0; - } - - $count = 0; - foreach ($this->getColumns() as $column => $title) { - if (isset($summary->$column) && $summary->$column > 0) { - $this->titles[] = sprintf($title, $summary->$column); - $count += $summary->$column; + if ($this->count === null) { + try { + $summary = self::summary($this->getDataView()); + } catch (Exception $_) { + $this->count = 0; } + $count = 0; + $titles = array(); + foreach ($this->getColumns() as $column => $title) { + if (isset($summary->$column) && $summary->$column > 0) { + $titles[] = sprintf($title, $summary->$column); + $count += $summary->$column; + } + } + $this->count = $count; + $this->title = implode('. ', $titles); } - return $count; + return $this->count; } } diff --git a/modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php b/modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php new file mode 100644 index 000000000..2cf25ea0a --- /dev/null +++ b/modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php @@ -0,0 +1,295 @@ +uri = $uri; + $request->method = 'POST'; + return $request; + } + + /** + * Send content type JSON + * + * @return $this + */ + public function sendJson() + { + $this->contentType = 'application/json'; + + return $this; + } + + /** + * Set basic auth credentials + * + * @param string $username + * @param string $password + * + * @return $this + */ + public function authenticateWith($username, $password) + { + $this->hasBasicAuth = true; + $this->username = $username; + $this->password = $password; + + return $this; + } + + /** + * Set request payload + * + * @param mixed $payload + * + * @return $this + */ + public function setPayload($payload) + { + $this->payload = $payload; + + return $this; + } + + /** + * Disable strict SSL + * + * @return $this + */ + public function noStrictSsl() + { + $this->strictSsl = false; + + return $this; + } + + /** + * Serialize payload according to content type + * + * @param mixed $payload + * @param string $contentType + * + * @return string + */ + public function serializePayload($payload, $contentType) + { + switch ($contentType) { + case 'application/json': + $payload = json_encode($payload); + break; + } + + return $payload; + } + + /** + * Send the request + * + * @return mixed + * + * @throws Exception + */ + public function send() + { + $defaults = array( + 'host' => 'localhost', + 'path' => '/' + ); + + $url = array_merge($defaults, parse_url($this->uri)); + + if (isset($url['port'])) { + $url['host'] .= sprintf(':%u', $url['port']); + } + + if (isset($url['query'])) { + $url['path'] .= sprintf('?%s', $url['query']); + } + + $headers = array( + "{$this->method} {$url['path']} HTTP/1.1", + "Host: {$url['host']}", + "Content-Type: {$this->contentType}", + 'Accept: application/json', + // Bypass "Expect: 100-continue" timeouts + 'Expect:' + ); + + $ch = curl_init(); + + $options = array( + CURLOPT_URL => $this->uri, + CURLOPT_TIMEOUT => $this->timeout, + // Ignore proxy settings + CURLOPT_PROXY => '', + CURLOPT_CUSTOMREQUEST => $this->method, + CURLOPT_RETURNTRANSFER => true + ); + + // Record cURL command line for debugging + $curlCmd = array('curl', '-s', '-X', $this->method, '-H', escapeshellarg('Accept: application/json')); + + if ($this->strictSsl) { + $options[CURLOPT_SSL_VERIFYHOST] = 2; + $options[CURLOPT_SSL_VERIFYPEER] = true; + } else { + $options[CURLOPT_SSL_VERIFYHOST] = false; + $options[CURLOPT_SSL_VERIFYPEER] = false; + $curlCmd[] = '-k'; + } + + if ($this->hasBasicAuth) { + $options[CURLOPT_USERPWD] = sprintf('%s:%s', $this->username, $this->password); + $curlCmd[] = sprintf('-u %s:%s', escapeshellarg($this->username), escapeshellarg($this->password)); + } + + if (! empty($this->payload)) { + $payload = $this->serializePayload($this->payload, $this->contentType); + $options[CURLOPT_POSTFIELDS] = $payload; + $curlCmd[] = sprintf('-d %s', escapeshellarg($payload)); + } + + $options[CURLOPT_HTTPHEADER] = $headers; + + $stream = null; + if (Logger::getInstance()->getLevel() === Logger::DEBUG) { + $stream = fopen('php://temp', 'w'); + $options[CURLOPT_VERBOSE] = true; + $options[CURLOPT_STDERR] = $stream; + } + + curl_setopt_array($ch, $options); + + Logger::debug( + 'Executing %s %s', + implode(' ', $curlCmd), + escapeshellarg($this->uri) + ); + + $result = curl_exec($ch); + + if ($result === false) { + throw new Exception(curl_error($ch)); + } + + curl_close($ch); + + if (is_resource($stream)) { + rewind($stream); + Logger::debug(stream_get_contents($stream)); + fclose($stream); + } + + $response = @json_decode($result, true); + + if ($response === null) { + if (version_compare(PHP_VERSION, '5.5.0', '>=')) { + throw new Exception(json_last_error_msg()); + } else { + switch (json_last_error()) { + case JSON_ERROR_DEPTH: + $msg = 'The maximum stack depth has been exceeded'; + break; + case JSON_ERROR_CTRL_CHAR: + $msg = 'Control character error, possibly incorrectly encoded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $msg = 'Invalid or malformed JSON'; + break; + case JSON_ERROR_SYNTAX: + $msg = 'Syntax error'; + break; + case JSON_ERROR_UTF8: + $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + default: + $msg = 'An error occured when parsing a JSON string'; + } + throw new Exception($msg); + } + } + + return $response; + } +} diff --git a/modules/monitoring/module.info b/modules/monitoring/module.info index f7e69d53a..1d43bee2a 100644 --- a/modules/monitoring/module.info +++ b/modules/monitoring/module.info @@ -1,5 +1,5 @@ Module: monitoring -Version: 2.3.2 +Version: 2.3.4 Description: Icinga monitoring module This is the core module for most Icingaweb users. It provides an abstraction layer for various Icinga data backends. diff --git a/modules/monitoring/public/css/module.less b/modules/monitoring/public/css/module.less index af2458b8b..4aec39e23 100644 --- a/modules/monitoring/public/css/module.less +++ b/modules/monitoring/public/css/module.less @@ -1,19 +1,42 @@ /*! Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ -// Styles for the icon displayed if a check source is reachable -.check-source-reachable { - color: @color-ok; +// Styles for the bottom status bar in the host and service list views +.monitoring-statusbar-ghost { + display: block; + visibility: hidden; } -// Styles for the icon displayed if a check source is not reachable -.check-source-not-reachable { - color: @color-critical; +.monitoring-statusbar { + background-color: @body-bg-color; + border-top: 1px solid @gray-lighter; + bottom: 0; + padding: .25em @gutter; + position: fixed; + + &:before { + background: -webkit-linear-gradient(bottom, @body-bg-color 0%, rgba(255,255,255,0) 100%); + background: -moz-linear-gradient(bottom, @body-bg-color 0%, rgba(255,255,255,0) 100%); + background: -ms-linear-gradient(bottom, @body-bg-color 0%, rgba(255,255,255,0) 100%); + background: -o-linear-gradient(bottom, @body-bg-color 0%, rgba(255,255,255,0) 100%); + background: linear-gradient(bottom, @body-bg-color 0%, rgba(255,255,255,0) 100%); + content: ""; + display: block; + height: 6px; + position: absolute; + left: 0; + right: 0; + top: -7px; + } + + .services-summary, + .hosts-summary { + float: left; + margin-bottom: 0; + } } // Styles for the icon displayed if a check result is late .check-result-late { - color: @color-critical; - &:before { // Remove right margin because the check now form may be displayed right next to the icon and we already have a gap // because of inline-blocks @@ -81,6 +104,7 @@ // Host and service summaries in detail and list views .hosts-summary, .services-summary { + display: inline-block; margin-bottom: 0.5em; > .hosts-link, @@ -90,11 +114,6 @@ } } -.controls > .hosts-summary, -.controls > .services-summary { - float: left; -} - // State table in the host and service multi-selection and detail views .host-detail-state, .service-detail-state { @@ -104,7 +123,23 @@ .grid { .hosts-summary, .services-summary { - .pull-left(); + float: left; + } +} + +// Quick actions +.quick-actions { + li { + color: @icinga-blue; + padding: 0 0.6em; + } + + li:first-child { + padding-left: 0; + } + + li:last-child { + padding-right: 0; } } @@ -319,10 +354,6 @@ div.box.stats { color: @gray; } -.stats > .pull-left { - float: none; -} - .stats > h3 { color: @gray; text-align: left; @@ -395,12 +426,6 @@ div.timeline { position: absolute; top: 50%; - &.extrapolated { - border-width: 2px; - border-style: dotted; - border-radius: 100%; - } - a.inner-circle { display: block; position: absolute; @@ -423,6 +448,61 @@ div.timeline { } } +@timeline-notification-color: #3a71ea; +@timeline-hard-state-color: #ff7000; +@timeline-comment-color: #79bdba; +@timeline-ack-color: #a2721d; +@timeline-downtime-start-color: #8e8e8e; +@timeline-downtime-end-color: #d5d6ad; + +.timeline-notification { + background-color: @timeline-notification-color; + + &.extrapolated { + background-color: lighten(@timeline-notification-color, 20%); + } +} + +.timeline-hard-state { + background-color: @timeline-hard-state-color; + + &.extrapolated { + background-color: lighten(@timeline-hard-state-color, 20%); + } +} + +.timeline-comment { + background-color: @timeline-comment-color; + + &.extrapolated { + background-color: lighten(@timeline-comment-color, 20%); + } +} + +.timeline-ack { + background-color: @timeline-ack-color; + + &.extrapolated { + background-color: lighten(@timeline-ack-color, 20%); + } +} + +.timeline-downtime-start { + background-color: @timeline-downtime-start-color; + + &.extrapolated { + background-color: lighten(@timeline-downtime-start-color, 20%); + } +} + +.timeline-downtime-end { + background-color: @timeline-downtime-end-color; + + &.extrapolated { + background-color: lighten(@timeline-downtime-end-color, 20%); + } +} + /* End of monitoring timeline styles */ /* Object features */ diff --git a/modules/monitoring/public/css/service-grid.less b/modules/monitoring/public/css/service-grid.less index 1b6e1eaf0..49409524a 100644 --- a/modules/monitoring/public/css/service-grid.less +++ b/modules/monitoring/public/css/service-grid.less @@ -5,24 +5,10 @@ white-space: nowrap; td { + color: @gray-light; + padding: 0.2em; text-align: center; - width: 1.0em; - color: @gray-lighter; - padding: 1px 2px; - - a { - .rounded-corners(0.4em); - display: block; - height: 1.5em; - width: 1.5em; - } - - i { - .rounded-corners(0.4em); - display: block; - height: 1.5em; - width: 1.5em; - } + width: 1em; } .rotate-45 { @@ -42,6 +28,26 @@ } } -.joystick-pagination a { - color: @text-color; +.joystick-pagination { + margin: 0 auto; + + a { + color: @text-color; + } + + i { + display: block; + height: 1.5em; + width: 1.5em; + } +} + +.service-grid-link { + .bg-stateful(); + .rounded-corners(); + + display: inline-block; + height: 1.5em; + vertical-align: middle; + width: 1.5em; } diff --git a/modules/monitoring/public/css/tables.less b/modules/monitoring/public/css/tables.less index 5f0d4f49a..a304c666d 100644 --- a/modules/monitoring/public/css/tables.less +++ b/modules/monitoring/public/css/tables.less @@ -108,7 +108,9 @@ // Table for performance data in detail views .performance-data-table { - width: 100%; + display: block; + overflow-x: auto; + position: relative; > thead > tr > th { text-align: left; @@ -119,6 +121,11 @@ // Reset base padding padding-left: 0; } + + > thead > tr > th, + > tbody > tr > td { + white-space: nowrap; + } } // Performance data table column for sparkline pie charts in detail views diff --git a/modules/monitoring/test/php/regression/Bug11728Test.php b/modules/monitoring/test/php/regression/Bug11728Test.php new file mode 100644 index 000000000..90b4cbe10 --- /dev/null +++ b/modules/monitoring/test/php/regression/Bug11728Test.php @@ -0,0 +1,25 @@ +assertTrue(strpos($helper->pluginOutput('A,BC', true), 'BC') !== false); + } +} diff --git a/modules/monitoring/test/php/regression/Bug11796Test.php b/modules/monitoring/test/php/regression/Bug11796Test.php new file mode 100644 index 000000000..f024334b7 --- /dev/null +++ b/modules/monitoring/test/php/regression/Bug11796Test.php @@ -0,0 +1,27 @@ +assertTrue( + strpos($helper->pluginOutput('EXAMPLE.COM', true), 'example.com') !== false + ); + } +} diff --git a/modules/setup/application/forms/AdminAccountPage.php b/modules/setup/application/forms/AdminAccountPage.php index 439a3beb2..3252ec160 100644 --- a/modules/setup/application/forms/AdminAccountPage.php +++ b/modules/setup/application/forms/AdminAccountPage.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Setup\Forms; use Exception; use Icinga\Application\Config; +use Icinga\Authentication\User\ExternalBackend; use Icinga\Authentication\User\UserBackend; use Icinga\Authentication\User\DbUserBackend; use Icinga\Authentication\User\LdapUserBackend; @@ -269,8 +270,8 @@ class AdminAccountPage extends Form */ protected function getUsername() { - $name = getenv('REMOTE_USER'); - if ($name === false) { + list($name, $_) = ExternalBackend::getRemoteUserInformation(); + if ($name === null) { return ''; } diff --git a/modules/setup/application/forms/AuthenticationPage.php b/modules/setup/application/forms/AuthenticationPage.php index 132f9377b..52e3c66f8 100644 --- a/modules/setup/application/forms/AuthenticationPage.php +++ b/modules/setup/application/forms/AuthenticationPage.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Setup\Forms; +use Icinga\Authentication\User\ExternalBackend; use Icinga\Web\Form; use Icinga\Application\Platform; @@ -30,15 +31,18 @@ class AuthenticationPage extends Form */ public function createElements(array $formData) { - if (isset($formData['type']) && $formData['type'] === 'external' && getenv('REMOTE_USER') === false) { - $this->info( - $this->translate( - 'You\'re currently not authenticated using any of the web server\'s authentication ' - . 'mechanisms. Make sure you\'ll configure such, otherwise you\'ll not be able to ' - . 'log into Icinga Web 2.' - ), - false - ); + if (isset($formData['type']) && $formData['type'] === 'external') { + list($username, $_) = ExternalBackend::getRemoteUserInformation(); + if ($username === null) { + $this->info( + $this->translate( + 'You\'re currently not authenticated using any of the web server\'s authentication ' + . 'mechanisms. Make sure you\'ll configure such, otherwise you\'ll not be able to ' + . 'log into Icinga Web 2.' + ), + false + ); + } } $backendTypes = array(); diff --git a/modules/setup/application/views/scripts/index/index.phtml b/modules/setup/application/views/scripts/index/index.phtml index ea0b61883..2c07e69fb 100644 --- a/modules/setup/application/views/scripts/index/index.phtml +++ b/modules/setup/application/views/scripts/index/index.phtml @@ -27,7 +27,7 @@ if ($notifications->hasMessages()) { ?>
- img('img/logo_icinga_big.png'); ?> + img('img/icinga-logo-big.png'); ?>

translate('Welcome', 'setup.progress'); ?>

diff --git a/modules/setup/module.info b/modules/setup/module.info index 7ca7e6958..10729ab94 100644 --- a/modules/setup/module.info +++ b/modules/setup/module.info @@ -1,5 +1,5 @@ Module: setup -Version: 2.3.2 +Version: 2.3.4 Description: Setup module Web based wizard for setting up Icinga Web 2 and its modules. This includes the data backends (e.g. relational database, LDAP), diff --git a/modules/test/module.info b/modules/test/module.info index 03ad30875..609fef5fc 100644 --- a/modules/test/module.info +++ b/modules/test/module.info @@ -1,5 +1,5 @@ Module: test -Version: 2.3.2 +Version: 2.3.4 Description: Translation module This module allows developers to run (unit) tests against Icinga Web 2 and any of its modules. Usually you do not need to enable this. diff --git a/modules/translation/public/img/doc/poedit_001.png b/modules/translation/doc/img/poedit_001.png similarity index 100% rename from modules/translation/public/img/doc/poedit_001.png rename to modules/translation/doc/img/poedit_001.png diff --git a/modules/translation/public/img/doc/poedit_002.png b/modules/translation/doc/img/poedit_002.png similarity index 100% rename from modules/translation/public/img/doc/poedit_002.png rename to modules/translation/doc/img/poedit_002.png diff --git a/modules/translation/public/img/doc/poedit_003.png b/modules/translation/doc/img/poedit_003.png similarity index 100% rename from modules/translation/public/img/doc/poedit_003.png rename to modules/translation/doc/img/poedit_003.png diff --git a/modules/translation/public/img/doc/poedit_004.png b/modules/translation/doc/img/poedit_004.png similarity index 100% rename from modules/translation/public/img/doc/poedit_004.png rename to modules/translation/doc/img/poedit_004.png diff --git a/modules/translation/public/img/doc/poedit_005.png b/modules/translation/doc/img/poedit_005.png similarity index 100% rename from modules/translation/public/img/doc/poedit_005.png rename to modules/translation/doc/img/poedit_005.png diff --git a/modules/translation/doc/translation.md b/modules/translation/doc/translation.md index 1a893f72c..fc2fbd841 100644 --- a/modules/translation/doc/translation.md +++ b/modules/translation/doc/translation.md @@ -93,15 +93,15 @@ configuration under the Preferences. __Personalize__: Please provide your Name and E-Mail under Identity. -![Personalize](img/translation/doc/poedit_001.png) +![Personalize](img/poedit_001.png) __Editor__: Under the Behavior the Automatically compile .mo files on save, should be disabled. -![Editor](/img/translation/doc/poedit_002.png) +![Editor](img/poedit_002.png) __Translations Memory__: Under the Database please add your languages, for which are you writing translations. -![Translations Memory](img/translation/doc/poedit_003.png) +![Translations Memory](img/poedit_003.png) When you are done, just save your new settings. @@ -113,12 +113,12 @@ translation strings for the core application. Each module names its translation module called __yourmodule__ the .po translation file will be named `yourmodule.po`. -![Full list of strings](img/translation/doc/poedit_004.png) +![Full list of strings](img/poedit_004.png) Now you can make changes and when there is no translation available, Poedit would mark it with a blue color, as shown below. -![Untranslated strings](img/translation/doc/poedit_005.png) +![Untranslated strings](img/poedit_005.png) And when you want to test your changes, please read more about under the chapter [Testing Translations](Testing Translations). diff --git a/modules/translation/module.info b/modules/translation/module.info index 5ae61f7d5..817194a66 100644 --- a/modules/translation/module.info +++ b/modules/translation/module.info @@ -1,5 +1,5 @@ Module: translation -Version: 2.3.2 +Version: 2.3.4 Description: Translation module This module allows developers and translators to translate Icinga Web 2 and its modules for multiple languages. You do not need this module to run an diff --git a/packages/RPM.md b/packages/RPM.md index 9c8e4a548..7a343bb5d 100644 --- a/packages/RPM.md +++ b/packages/RPM.md @@ -63,7 +63,7 @@ Decide whether to use MySQL or PostgreSQL. postgres=# CREATE DATABASE icingaweb; postgres=# \q -Add the `cingaweb` user for trusted authentication to your `pg_hba.conf` file +Add the `icingaweb` user for trusted authentication to your `pg_hba.conf` file in `/var/lib/pgsql/data/pg_hba.conf` and restart the PostgreSQL server. local icingaweb icingaweb trust diff --git a/public/css/icinga/badges.less b/public/css/icinga/badges.less index 86a4b4349..b9c154349 100644 --- a/public/css/icinga/badges.less +++ b/public/css/icinga/badges.less @@ -5,7 +5,9 @@ @badge-padding: 0.25em; .badge { + .bg-stateful(); .rounded-corners(); + background-color: @gray-light; color: @badge-color; display: inline-block; @@ -17,36 +19,4 @@ text-align: center; vertical-align: middle; white-space: nowrap; - - &.state-ok { - .bg-color-ok(); - } - - &.state-up { - .bg-color-up(); - } - - &.state-warning { - .bg-color-warning(); - } - - &.state-critical { - .bg-color-critical(); - } - - &.state-down { - .bg-color-down(); - } - - &.state-unreachable { - .bg-color-unreachable(); - } - - &.state-unknown { - .bg-color-unknown(); - } - - &.state-pending { - .bg-color-pending(); - } } diff --git a/public/css/icinga/base.less b/public/css/icinga/base.less index 2491dad7a..dcacb0171 100644 --- a/public/css/icinga/base.less +++ b/public/css/icinga/base.less @@ -12,14 +12,17 @@ // Icinga colors @icinga-blue: #0095BF; @color-ok: #44bb77; +@color-up: @color-ok; @color-warning: #ffaa44; @color-warning-handled: #ffcc66; @color-critical: #ff5566; @color-critical-handled: #ff99aa; +@color-down: @color-critical; +@color-down-handled: @color-critical-handled; @color-unknown: #aa44ff; @color-unknown-handled: #cc77ff; -@color-unreachable: #aa44ff; -@color-unreachable-handled: #cc77ff; +@color-unreachable: @color-unknown; +@color-unreachable-handled: @color-unknown-handled; @color-pending: #77aaff; // Notification colors @@ -44,7 +47,7 @@ @tr-hover-color: #F5FDFF; // Font families -@font-family: Calibri, "Lucida Grande", sans-serif; +@font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; @font-family-fixed: "Liberation Mono", "Lucida Console", Courier, monospace; @font-family-wide: Tahoma, Verdana, sans-serif; @@ -88,9 +91,8 @@ a { a:focus, button:focus, input[type="checkbox"]:focus { - outline-color: @icinga-blue; - outline-style: solid; // IE - outline-style: auto; + outline: 3px solid fade(@icinga-blue, 50%); + outline-offset: 1px; } // Default margin for block text diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index e4cf350c3..0ac312314 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -53,10 +53,6 @@ input::-webkit-input-placeholder { color: inherit; } -input[placeholder] { - color: inherit; -} - input.search { background: transparent url("../img/icons/search.png") no-repeat scroll 0.1em center; background-size: 1em 1em; @@ -68,8 +64,9 @@ input.search { } } -// TODO(el): .form-inline control-group { display: inline-block; } -form.inline { +// TODO(el): .form-controls-inline control-group { display: inline-block; } +form.inline, +.form-inline { .control-group { padding: 0; } diff --git a/public/css/icinga/grid.less b/public/css/icinga/grid.less index 76ad0855f..e061bc8aa 100644 --- a/public/css/icinga/grid.less +++ b/public/css/icinga/grid.less @@ -6,7 +6,7 @@ [class^="col-"], [class*=" col-"] { - .pull-left(); + float: left; // Fix that empty columns don't consume their width min-height: 1px; } diff --git a/public/css/icinga/layout-structure.less b/public/css/icinga/layout-structure.less index 8950ef324..6bca47f25 100644 --- a/public/css/icinga/layout-structure.less +++ b/public/css/icinga/layout-structure.less @@ -13,7 +13,7 @@ html { } #header { - height: 4em; + height: 3em; left: 0; position: fixed; top: 0; @@ -24,7 +24,7 @@ html { bottom: 0; left: 0; position: fixed; - top: 4em; + top: 3em; width: 12em; } @@ -33,7 +33,7 @@ html { left: 12em; position: fixed; right: 0; - top: 4em; + top: 3em; } .iframe { @@ -112,6 +112,7 @@ html { } } +.controls-separated, .container .controls.separated { box-shadow: 0 3px 4px -4px rgba(0, 0, 0, 0.2); // border-bottom: 1px solid @gray-lightest; diff --git a/public/css/icinga/layout.less b/public/css/icinga/layout.less index ca090326e..8156800ac 100644 --- a/public/css/icinga/layout.less +++ b/public/css/icinga/layout.less @@ -37,7 +37,7 @@ } #header-logo-container { - height: 4em; + height: 3em; margin-bottom: 0.2em; margin-left: 1.5em; margin-top: 0.2em; @@ -45,8 +45,9 @@ } #header-logo { - background-image: url('../img/logo_icinga-inv.png'); - background-position: center; + background-image: url('../img/icinga-logo.png'); + background-image: url('../img/icinga-logo.svg'); + background-position: left center; background-repeat: no-repeat; background-size: contain; display: block; @@ -54,8 +55,9 @@ } #icinga-logo { - background-image: url('../img/logo_icinga_big.png'); - background-position: center; + background-image: url('../img/icinga-logo-big.png'); + background-image: url('../img/icinga-logo-big.svg'); + background-position: center bottom; background-repeat: no-repeat; background-size: contain; // Does not work in IE < 10 height: 177px; diff --git a/public/css/icinga/login.less b/public/css/icinga/login.less index 9a91a87ef..1da2690ed 100644 --- a/public/css/icinga/login.less +++ b/public/css/icinga/login.less @@ -55,6 +55,7 @@ .rounded-corners(3px); background-color: @body-bg-color; border: none; + border-radius: 0; color: @icinga-blue; padding-bottom: 3px; } diff --git a/public/css/icinga/main.less b/public/css/icinga/main.less index 8737c5c99..429d1a6f8 100644 --- a/public/css/icinga/main.less +++ b/public/css/icinga/main.less @@ -34,10 +34,6 @@ white-space: pre-wrap; } -.pull-left { - float: left; -} - .pull-right { float: right; } @@ -65,6 +61,10 @@ a:hover > .icon-cancel { color: @color-critical; } +.icon-stateful { + .fg-stateful(); +} + // Link styles .button-link { @@ -206,7 +206,7 @@ a:hover > .icon-cancel { .centered-ghost { height: 100%; text-align: center; - letter-spacing: -1em; // Remove gap between content and ghost + letter-spacing: -0.417em; // Remove gap between content and ghost } .centered-ghost > * { diff --git a/public/css/icinga/menu.less b/public/css/icinga/menu.less index 61e3e0bb9..2f52f67fd 100644 --- a/public/css/icinga/menu.less +++ b/public/css/icinga/menu.less @@ -17,7 +17,7 @@ #menu a { &:focus { color: @icinga-blue; - outline-offset: -0.25em; + outline-offset: -3px; } &:hover { @@ -138,6 +138,7 @@ > .nav-item { display: block; + margin-left: 0.5em; > a:hover { color: @text-color-inverted; @@ -153,11 +154,11 @@ #layout.hoveredmenu { #main { - z-index: 2; + z-index: 2; } #sidebar { - z-index: 3; + z-index: 3; } #menu { @@ -166,7 +167,6 @@ } // Accessibility skip links - .skip-links { position: relative; ul { @@ -177,13 +177,15 @@ display: block; a, button[type="submit"] { background-color: @body-bg-color; + border: none; left: -999px; padding: @vertical-padding @horizontal-padding; position: absolute; width: 100%; + z-index: 1; &:focus { left: 0; - outline: 1px dotted black; + outline-offset: -3px; } } button[type="submit"] { @@ -197,3 +199,71 @@ bottom: 0 !important; height: auto !important; } + +.search-control { + position: relative; +} + +.search-reset { + background: none; + border: 0; + cursor: pointer; + display: none; + height: 100%; + padding: 0; + position: absolute; + right: 0; + top: 0; + user-select: none; +} + +.search-reset:focus { + outline: 0; +} + +// Override forms.less +input[type=text].search-input { + padding-right: 1.4em; + text-overflow: ellipsis; + transition: none; +} + +.search-input:focus:-moz-placeholder { // FF 18- + color: @gray-light; +} + +.search-input:focus::-moz-placeholder { // FF 19+ + color: @gray-light; +} + +.search-input:focus:-ms-input-placeholder { + color: @gray-light; +} + +.search-input:focus::-webkit-input-placeholder { + color: @gray-light; +} + +.search-input:valid ~ .search-reset { + animation-duration: .4s; + animation-name: search-reset-in; + display: block; +} + +.search-input:invalid, +.search-input:-moz-submit-invalid, +.search-input:-moz-ui-invalid { + // Disable glow + box-shadow: none; +} + +@keyframes search-reset-in { + 0% { + opacity: 0; + transform: translate3d(-20%, 0, 0); + } + 100% { + opacity: 1; + transform: none; + } +} diff --git a/public/css/icinga/mixins.less b/public/css/icinga/mixins.less index 33c6fe022..984f51de2 100644 --- a/public/css/icinga/mixins.less +++ b/public/css/icinga/mixins.less @@ -16,7 +16,7 @@ outline: none; padding: @vertical-padding @horizontal-padding; - // Transition mixin does not work w/ comma-separated transistions + // Transition mixin does not work w/ comma-separated transitions -webkit-transition: background 0.3s ease, color 0.3s ease; -moz-transition: background 0.3s ease, color 0.3s ease; -o-transition: background 0.3s ease, color 0.3s ease; @@ -42,9 +42,7 @@ } .opacity(@opacity: 0.6) { - -webkit-opacity: @opacity; - -moz-opacity: @opacity; - opacity: @opacity; + opacity: @opacity; } .transform(@transform) { @@ -116,88 +114,88 @@ animation-fill-mode: forwards; } -// Color mixins - -.bg-color-ok, -.bg-color-up { - background-color: @color-ok; -} - -.bg-color-warning { - background-color: @color-warning; - - &.handled { - background-color: @color-warning-handled; +// Mixin for stateful foreground colors, e.g. text or icons +.fg-stateful { + &.state-ok { + color: @color-ok; + } + &.state-up { + color: @color-up; + } + &.state-warning { + color: @color-warning; + &.handled { + color: @color-warning-handled; + } + } + &.state-critical { + color: @color-critical; + &.handled { + color: @color-critical-handled; + } + } + &.state-down { + color: @color-down; + &.handled { + color: @color-down-handled; + } + } + &.state-unreachable { + color: @color-unreachable; + &.handled { + color: @color-unreachable-handled; + } + } + &.state-unknown { + color: @color-unknown; + &.handled { + color: @color-unknown-handled; + } + } + &.state-pending { + color: @color-pending; } } -.bg-color-critical, -.bg-color-down { - background-color: @color-critical; - - &.handled { - background-color: @color-critical-handled; +// Mixin for stateful background colors +.bg-stateful { + &.state-ok { + background-color: @color-ok; + } + &.state-up { + background-color: @color-up; + } + &.state-warning { + background-color: @color-warning; + &.handled { + background-color: @color-warning-handled; + } + } + &.state-critical { + background-color: @color-critical; + &.handled { + background-color: @color-critical-handled; + } + } + &.state-down { + background-color: @color-down; + &.handled { + background-color: @color-down-handled; + } + } + &.state-unreachable { + background-color: @color-unreachable; + &.handled { + background-color: @color-unreachable-handled; + } + } + &.state-unknown { + background-color: @color-unknown; + &.handled { + background-color: @color-unknown-handled; + } + } + &.state-pending { + background-color: @color-pending; } } - -.bg-color-unreachable { - background-color: @color-unreachable; - - &.handled { - background-color: @color-unreachable-handled; - } -} - -.bg-color-unknown { - background-color: @color-unknown; - - &.handled { - background-color: @color-unknown-handled; - } -} - -.bg-color-pending { - background-color: @color-pending; -} - -.fg-color-ok, -.fg-color-up { - color: @color-ok; -} - -.fg-color-warning { - color: @color-warning; - - &.handled { - color: @color-warning-handled; - } -} - -.fg-color-critical, -.fg-color-down { - color: @color-critical; - - &.handled { - color: @color-critical-handled; - } -} - -.fg-color-unreachable { - color: @color-unreachable; - - &.handled { - color: @color-unreachable-handled; - } -} - -.fg-color-unknown { - color: @color-unknown; - - &.handled { - color: @color-unknown-handled; - } -} - -.fg-color-pending { - color: @color-pending; -} diff --git a/public/css/icinga/responsive.less b/public/css/icinga/responsive.less index 7c759c1ec..2d23a2562 100644 --- a/public/css/icinga/responsive.less +++ b/public/css/icinga/responsive.less @@ -102,7 +102,7 @@ } #layout.twocols #col2 { - border-left: 1px solid @gray-light; + border-left: 1px solid @gray-lighter; display: block; } diff --git a/public/css/icinga/widgets.less b/public/css/icinga/widgets.less index 55257e89d..ad5fd7432 100644 --- a/public/css/icinga/widgets.less +++ b/public/css/icinga/widgets.less @@ -1,5 +1,50 @@ /*! Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ +#announcements > ul { + background-color: @body-bg-color; + font-size: @font-size; + list-style: none; + margin: 0; + padding: 0; + + > li:before { + color: @icinga-blue; + content: "\e811"; + font-family: 'ifont'; + left: 1em; + margin-top: -0.8em; + padding: 0.3em; + position: absolute; + text-align: center; + top: 50%; + } + + > li { + border-bottom: 1px solid @gray-lighter; + padding: 1em 3em; + position: relative; + } + + > li .message { + display: inline-block; + vertical-align: middle; + } + + > li:last-child { + border-bottom: none; + } + + .acknowledge-announcement-control { + background: none; + border: none; + display: block; + margin-top: -0.6em; + position: absolute; + right: 1em; + top: 50%; + } +} + table.historycolorgrid { font-size: 1.5em; } diff --git a/public/css/pdf/pdfprint.less b/public/css/pdf/pdfprint.less index 39c3985b9..edfb0e516 100644 --- a/public/css/pdf/pdfprint.less +++ b/public/css/pdf/pdfprint.less @@ -1,9 +1,6 @@ /*! Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ -.controls form, -.controls .pagination, -.controls .widgetLimiter, -.controls > .tabs, +.controls, .dontprint, // Compat only, use dont-print instead .dont-print { display: none !important; diff --git a/public/css/themes/high-contrast.less b/public/css/themes/high-contrast.less index d5db9795c..c9057c08d 100644 --- a/public/css/themes/high-contrast.less +++ b/public/css/themes/high-contrast.less @@ -16,7 +16,6 @@ @text-color: #191919; @text-color-light: #555555; - .badge { background-color: @text-color-light; } @@ -64,3 +63,63 @@ color: @text-color; text-decoration: underline; } + +.icinga-module.module-monitoring { + @timeline-notification-color: #1650CF; + @timeline-hard-state-color: #A24600; + @timeline-comment-color: #346964; + @timeline-ack-color: #855D18; + @timeline-downtime-start-color: #515151; + @timeline-downtime-end-color: #5e5e2f; + + // Unfortunately it does not suffice to only override the timeline colors here, because our less compiler seems to + // have the related style block in module.less already evaluated + + .timeline-notification { + background-color: @timeline-notification-color; + + &.extrapolated { + background-color: lighten(@timeline-notification-color, 20%); + } + } + + .timeline-hard-state { + background-color: @timeline-hard-state-color; + + &.extrapolated { + background-color: lighten(@timeline-hard-state-color, 20%); + } + } + + .timeline-comment { + background-color: @timeline-comment-color; + + &.extrapolated { + background-color: lighten(@timeline-comment-color, 20%); + } + } + + .timeline-ack { + background-color: @timeline-ack-color; + + &.extrapolated { + background-color: lighten(@timeline-ack-color, 20%); + } + } + + .timeline-downtime-start { + background-color: @timeline-downtime-start-color; + + &.extrapolated { + background-color: lighten(@timeline-downtime-start-color, 20%); + } + } + + .timeline-downtime-end { + background-color: @timeline-downtime-end-color; + + &.extrapolated { + background-color: lighten(@timeline-downtime-end-color, 20%); + } + } +} diff --git a/public/font/ifont.eot b/public/font/ifont.eot old mode 100644 new mode 100755 index c018a999b..73c634292 Binary files a/public/font/ifont.eot and b/public/font/ifont.eot differ diff --git a/public/font/ifont.svg b/public/font/ifont.svg old mode 100644 new mode 100755 index fd05fcf85..08357981a --- a/public/font/ifont.svg +++ b/public/font/ifont.svg @@ -1,140 +1,280 @@ -Copyright (C) 2015 by original authors @ fontello.com +Copyright (C) 2016 by original authors @ fontello.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/font/ifont.ttf b/public/font/ifont.ttf old mode 100644 new mode 100755 index e470fa4c0..041d8fbd0 Binary files a/public/font/ifont.ttf and b/public/font/ifont.ttf differ diff --git a/public/font/ifont.woff b/public/font/ifont.woff old mode 100644 new mode 100755 index 0c5b16def..3f3d33e32 Binary files a/public/font/ifont.woff and b/public/font/ifont.woff differ diff --git a/public/font/ifont.woff2 b/public/font/ifont.woff2 new file mode 100755 index 000000000..32b170427 Binary files /dev/null and b/public/font/ifont.woff2 differ diff --git a/public/img/favicon.png b/public/img/favicon.png index 6d6f71398..1c27fd55f 100644 Binary files a/public/img/favicon.png and b/public/img/favicon.png differ diff --git a/public/img/icinga-icon-inv.png b/public/img/icinga-icon-inv.png deleted file mode 100644 index 1d9d04ed7..000000000 Binary files a/public/img/icinga-icon-inv.png and /dev/null differ diff --git a/public/img/icinga-icon.png b/public/img/icinga-icon.png deleted file mode 100644 index 7540d2d30..000000000 Binary files a/public/img/icinga-icon.png and /dev/null differ diff --git a/public/img/icinga-logo-big-dark.png b/public/img/icinga-logo-big-dark.png new file mode 100644 index 000000000..c75d194a5 Binary files /dev/null and b/public/img/icinga-logo-big-dark.png differ diff --git a/public/img/icinga-logo-big.png b/public/img/icinga-logo-big.png new file mode 100644 index 000000000..90f64e4d5 Binary files /dev/null and b/public/img/icinga-logo-big.png differ diff --git a/public/img/icinga-logo-big.svg b/public/img/icinga-logo-big.svg new file mode 100644 index 000000000..740cbc8f1 --- /dev/null +++ b/public/img/icinga-logo-big.svg @@ -0,0 +1 @@ +icinga-logo-big diff --git a/public/img/icinga-logo.png b/public/img/icinga-logo.png new file mode 100644 index 000000000..670f45c1b Binary files /dev/null and b/public/img/icinga-logo.png differ diff --git a/public/img/icinga-logo.svg b/public/img/icinga-logo.svg new file mode 100644 index 000000000..49a2ef5e0 --- /dev/null +++ b/public/img/icinga-logo.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/img/logo_icinga-inv.png b/public/img/logo_icinga-inv.png deleted file mode 100644 index c2acc2369..000000000 Binary files a/public/img/logo_icinga-inv.png and /dev/null differ diff --git a/public/img/logo_icinga.png b/public/img/logo_icinga.png deleted file mode 100644 index 162bfdd69..000000000 Binary files a/public/img/logo_icinga.png and /dev/null differ diff --git a/public/img/logo_icinga_big.png b/public/img/logo_icinga_big.png deleted file mode 100644 index d92dc7f0c..000000000 Binary files a/public/img/logo_icinga_big.png and /dev/null differ diff --git a/public/img/logo_icinga_big_dark.png b/public/img/logo_icinga_big_dark.png deleted file mode 100644 index bf320133b..000000000 Binary files a/public/img/logo_icinga_big_dark.png and /dev/null differ diff --git a/public/img/touch-icon.png b/public/img/touch-icon.png new file mode 100644 index 000000000..c5d5fdd0e Binary files /dev/null and b/public/img/touch-icon.png differ diff --git a/public/js/icinga.js b/public/js/icinga.js index b52c19afc..6c5035912 100644 --- a/public/js/icinga.js +++ b/public/js/icinga.js @@ -69,10 +69,10 @@ */ this.modules = {}; - var self = this; + var _this = this; $(document).ready(function () { - self.initialize(); - self = null; + _this.initialize(); + _this = null; }); }; @@ -94,9 +94,9 @@ this.loader = new Icinga.Loader(this); this.events = new Icinga.Events(this); this.history = new Icinga.History(this); - var self = this; + var _this = this; $.each(Icinga.Behaviors, function(name, Behavior) { - self.behaviors[name.toLowerCase()] = new Behavior(self); + _this.behaviors[name.toLowerCase()] = new Behavior(_this); }); this.timezone.initialize(); diff --git a/public/js/icinga/behavior/actiontable.js b/public/js/icinga/behavior/actiontable.js index 8d8424999..a7fc06a36 100644 --- a/public/js/icinga/behavior/actiontable.js +++ b/public/js/icinga/behavior/actiontable.js @@ -185,12 +185,12 @@ filter.addClass('active'); return; } - var self = this; + var _this = this; this.rowActions() .filter( function (i, el) { - var params = self.getRowData($(el)); - if (self.icinga.utils.objectKeys(params).length !== self.icinga.utils.objectKeys(filter).length) { + var params = _this.getRowData($(el)); + if (_this.icinga.utils.objectKeys(params).length !== _this.icinga.utils.objectKeys(filter).length) { return false; } var equal = true; @@ -274,21 +274,21 @@ * @returns {String} The filter string */ toQuery: function() { - var self = this; + var _this = this; var selections = this.selections(); var queries = []; var utils = this.icinga.utils; if (selections.length === 1) { return $(selections[0]).attr('href'); - } else if (selections.length > 1 && self.hasMultiselection()) { + } else if (selections.length > 1 && _this.hasMultiselection()) { selections.each(function (i, el) { var parts = []; - $.each(self.getRowData($(el)), function(key, value) { + $.each(_this.getRowData($(el)), function(key, value) { parts.push(utils.fixedEncodeURIComponent(key) + '=' + utils.fixedEncodeURIComponent(value)); }); queries.push('(' + parts.join('&') + ')'); }); - return self.getMultiselectionUrl() + '?(' + queries.join('|') + ')'; + return _this.getMultiselectionUrl() + '?(' + queries.join('|') + ')'; } else { return ''; } @@ -304,9 +304,9 @@ var query = parseSelectionQuery(hash); if (query.length > 1 && this.hasMultiselectionUrl(this.icinga.utils.parseUrl(hash).path)) { // select all rows with matching filters - var self = this; + var _this = this; $.each(query, function(i, selection) { - self.select(selection); + _this.select(selection); }); } if (query.length > 1) { @@ -351,10 +351,10 @@ * Handle clicks on table rows and update selection and history */ ActionTable.prototype.onRowClicked = function (event) { - var self = event.data.self; + var _this = event.data.self; var $target = $(event.target); var $tr = $target.closest('tr'); - var table = new Selection($tr.closest('table.action, table.table-row-selectable')[0], self.icinga); + var table = new Selection($tr.closest('table.action, table.table-row-selectable')[0], _this.icinga); // some rows may contain form actions that trigger a different action, pass those through if (!$target.hasClass('rowaction') && $target.closest('form').length && @@ -390,18 +390,18 @@ var count = table.selections().length; if (count > 0) { var query = table.toQuery(); - self.icinga.loader.loadUrl(query, self.icinga.events.getLinkTargetFor($tr)); + _this.icinga.loader.loadUrl(query, _this.icinga.events.getLinkTargetFor($tr)); state += '#!' + query; } else { - if (self.icinga.events.getLinkTargetFor($tr).attr('id') === 'col2') { - self.icinga.ui.layout1col(); + if (_this.icinga.events.getLinkTargetFor($tr).attr('id') === 'col2') { + _this.icinga.ui.layout1col(); } } - self.icinga.history.pushUrl(state); + _this.icinga.history.pushUrl(state); // redraw all table selections - self.tables().each(function () { - new Selection(this, self.icinga).refresh(); + _this.tables().each(function () { + new Selection(this, _this.icinga).refresh(); }); // update selection info @@ -414,7 +414,7 @@ */ ActionTable.prototype.onRendered = function(evt) { var container = evt.target; - var self = evt.data.self; + var _this = evt.data.self; // initialize all rows with the correct row action $('table.action tr, table.table-row-selectable tr', container).each(function(idx, el) { @@ -442,20 +442,21 @@ }); // draw all active selections that have disappeared on reload - self.tables().each(function(i, el) { - new Selection(el, self.icinga).refresh(); + _this.tables().each(function(i, el) { + new Selection(el, _this.icinga).refresh(); }); // update displayed selection counter - var table = new Selection(self.tables(container).first()); + var table = new Selection(_this.tables(container).first()); $(container).find('.selection-info-count').text(table.selections().size()); }; ActionTable.prototype.clearAll = function () { - var self = this; + var _this = this; this.tables().each(function () { - new Selection(this, self.icinga).clear(); + new Selection(this, _this.icinga).clear(); }); + $('.selection-info-count').text('0'); }; Icinga.Behaviors.ActionTable = ActionTable; diff --git a/public/js/icinga/behavior/form.js b/public/js/icinga/behavior/form.js index 750f02223..279486e0a 100644 --- a/public/js/icinga/behavior/form.js +++ b/public/js/icinga/behavior/form.js @@ -60,10 +60,9 @@ if ($search[0] === document.activeElement) { return null; } - var search = $container.find('#search').val(); - if (search.length) { + if ($search.length) { var $content = $('
').append(content); - $content.find('#search').attr('value', search).addClass('active'); + $content.find('#search').attr('value', $search.val()).addClass('active'); return $content.html(); } return content; @@ -72,15 +71,15 @@ var origFocus = document.activeElement; var containerId = $container.attr('id'); var icinga = this.icinga; - var self = this.icinga.behaviors.form; + var _this = this.icinga.behaviors.form; var changed = false; $container.find('form').each(function () { - var form = self.uniqueFormName(this); + var form = _this.uniqueFormName(this); if (autorefresh) { // check if an element in this container was changed $(this).find('input').each(function () { var name = this.name; - if (self.inputs[form] && self.inputs[form][name]) { + if (_this.inputs[form] && _this.inputs[form][name]) { icinga.logger.debug( 'form input: ' + form + '.' + name + ' was changed and aborts reload...' ); @@ -89,7 +88,7 @@ }); } else { // user-triggered reload, forget all changes to forms in this container - self.inputs[form] = null; + _this.inputs[form] = null; } }); if (changed) { @@ -101,6 +100,7 @@ && ! $(origFocus).hasClass('autofocus') && ! $(origFocus).hasClass('autosubmit') && $(origFocus).closest('form').length + && $(origFocus).not(':input[type=button], :input[type=submit], :input[type=reset]').length ) { icinga.logger.debug('Not changing content for ' + containerId + ' form has focus'); return null; diff --git a/public/js/icinga/behavior/navigation.js b/public/js/icinga/behavior/navigation.js index 539639685..2da6bc87d 100644 --- a/public/js/icinga/behavior/navigation.js +++ b/public/js/icinga/behavior/navigation.js @@ -108,14 +108,14 @@ var $a = $(this); var href = $a.attr('href'); var $li; - var self = event.data.self; - var icinga = self.icinga; + var _this = event.data.self; + var icinga = _this.icinga; - self.hovered = null; + _this.hovered = null; if (href.match(/#/)) { // ...it may be a menu section without a dedicated link. // Switch the active menu item: - self.setActive($a); + _this.setActive($a); $li = $a.closest('li'); if ($li.hasClass('hover')) { $li.removeClass('hover'); @@ -128,7 +128,7 @@ return; } } else { - self.setActive($(event.target)); + _this.setActive($(event.target)); } // update target url of the menu container to the clicked link var $menu = $('#menu'); @@ -276,9 +276,9 @@ var $li = $(this), delay = 800, - self = event.data.self; + _this = event.data.self; - self.hovered = null; + _this.hovered = null; if ($li.hasClass('active')) { $li.siblings().removeClass('hover'); return; @@ -315,14 +315,14 @@ $sibling.removeClass('hover'); } }); - self.hoverElement($li); + _this.hoverElement($li); }, delay); }; Navigation.prototype.leaveSidebar = function (event) { var $sidebar = $(this), $li = $sidebar.find('li.hover'), - self = event.data.self; + _this = event.data.self; if (! $li.length) { $('#layout').removeClass('hoveredmenu'); return; @@ -337,7 +337,7 @@ $li.removeClass('hover'); $('#layout').removeClass('hoveredmenu'); }, 500); - self.hovered = null; + _this.hovered = null; }; Navigation.prototype.hoverElement = function ($li) { @@ -356,7 +356,7 @@ Navigation.prototype.dropdownLeave = function (event) { var $li = $(this), - self = event.data.self; + _this = event.data.self; setTimeout(function () { // TODO: make this behave well together with keyboard navigation try { @@ -365,7 +365,7 @@ } } catch(e) { /* Bypass because if IE8 */ } }, 300); - self.hovered = null; + _this.hovered = null; }; Icinga.Behaviors.Navigation = Navigation; diff --git a/public/js/icinga/behavior/tooltip.js b/public/js/icinga/behavior/tooltip.js index 98cc1ff59..abc1d4952 100644 --- a/public/js/icinga/behavior/tooltip.js +++ b/public/js/icinga/behavior/tooltip.js @@ -21,7 +21,7 @@ }; Tooltip.prototype.onRendered = function(evt) { - var self = evt.data.self, icinga = evt.data.icinga, el = evt.target; + var _this = evt.data.self, icinga = evt.data.icinga, el = evt.target; $('[title]', el).each(function () { var $el = $(this); @@ -63,7 +63,7 @@ return; } var title = $(this).find('.tipsy-inner').html(); - var atMouse = document.elementFromPoint(self.mouseX, self.mouseY); + var atMouse = document.elementFromPoint(_this.mouseX, _this.mouseY); var nearestTip = $(atMouse).closest('[original-title="' + title + '"]')[0]; if (nearestTip) { var tipsy = $.data(nearestTip, 'tipsy'); diff --git a/public/js/icinga/behavior/tristate.js b/public/js/icinga/behavior/tristate.js index 88c07102d..77dad14e1 100644 --- a/public/js/icinga/behavior/tristate.js +++ b/public/js/icinga/behavior/tristate.js @@ -13,7 +13,7 @@ Tristate.prototype = new Icinga.EventListener(); Tristate.prototype.clickTriState = function (event) { - var self = event.data.self; + var _this = event.data.self; var $tristate = $(this); var triState = parseInt($tristate.data('icinga-tristate'), 10); @@ -42,7 +42,7 @@ } else { $tristate.parent().find('b.tristate-changed').css('visibility', 'hidden'); } - self.icinga.ui.setTriState(value.toString(), $tristate); + _this.icinga.ui.setTriState(value.toString(), $tristate); }; Icinga.Behaviors.Tristate = Tristate; diff --git a/public/js/icinga/eventlistener.js b/public/js/icinga/eventlistener.js index a7415e2f8..786441fa5 100644 --- a/public/js/icinga/eventlistener.js +++ b/public/js/icinga/eventlistener.js @@ -41,14 +41,14 @@ * 'on' to register listeners */ EventListener.prototype.bind = function (emitter) { - var self = this; + var _this = this; $.each(this.handlers, function(i, handler) { - self.icinga.logger.debug('bind: ' + handler.evt + '(' + handler.cond + ')'); + _this.icinga.logger.debug('bind: ' + handler.evt + '(' + handler.cond + ')'); emitter.on( handler.evt, handler.cond, { self: handler.scope || emitter, - icinga: self.icinga + icinga: _this.icinga }, handler.fn ); }); @@ -61,9 +61,9 @@ * 'off' to un-register listeners. */ EventListener.prototype.unbind = function (emitter) { - var self = this; + var _this = this; $.each(this.handlers, function(i, handler) { - self.icinga.logger.debug('unbind: ' + handler.evt + '(' + handler.cond + ')'); + _this.icinga.logger.debug('unbind: ' + handler.evt + '(' + handler.cond + ')'); emitter.off(handler.evt, handler.cond, handler.fn); }); }; diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 829ede335..340d652b1 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -33,8 +33,8 @@ // TODO: What's this? applyHandlers: function (event) { var $target = $(event.target); - var self = event.data.self; - var icinga = self.icinga; + var _this = event.data.self; + var icinga = _this.icinga; if (! icinga) { // Attempt to catch a rare error, race condition, whatever @@ -42,7 +42,7 @@ return; } - if (self.initializeModules) { + if (_this.initializeModules) { var loaded = false; var moduleName = $target.data('icingaModule'); if (moduleName) { @@ -63,7 +63,7 @@ // so we need to ensure that it is called the first time they are // initialized event.stopImmediatePropagation(); - self.initializeModules = false; + _this.initializeModules = false; var $container = $target.closest('.container'); if (! $container.length) { @@ -79,7 +79,7 @@ return false; } } else { - self.initializeModules = true; + _this.initializeModules = true; } $('.dashboard > div', $target).each(function(idx, el) { @@ -93,7 +93,7 @@ var $searchField = $('#menu input.search', $target); // Remember initial search field value if any if ($searchField.length && $searchField.val().length) { - self.searchValue = $searchField.val(); + _this.searchValue = $searchField.val(); } }, @@ -105,7 +105,7 @@ // Note: It is important that this is the first handler for this event! $(document).on('rendered', { self: this }, this.applyHandlers); - $.each(self.icinga.behaviors, function (name, behavior) { + $.each(this.icinga.behaviors, function (name, behavior) { behavior.bind($(document)); }); @@ -129,6 +129,7 @@ $(document).on('click', 'a', { self: this }, this.linkClicked); $(document).on('click', 'tr[href]', { self: this }, this.linkClicked); + $(document).on('click', 'input[type="submit"], button[type="submit"]', this.rememberSubmitButton); // We catch all form submit events $(document).on('submit', 'form', { self: this }, this.submitForm); @@ -189,12 +190,18 @@ }, autoSubmitSearch: function(event) { - var self = event.data.self; - if ($('#menu input.search').val() === self.searchValue) { + var _this = event.data.self; + if ($('#menu input.search').val() === _this.searchValue) { return; } - self.searchValue = $('#menu input.search').val(); - return self.autoSubmitForm(event); + _this.searchValue = $('#menu input.search').val(); + return _this.autoSubmitForm(event); + }, + + rememberSubmitButton: function(e) { + var $button = $(this); + var $form = $button.closest('form'); + $form.data('submitButton', $button); }, autoSubmitForm: function (event) { @@ -205,8 +212,8 @@ * */ submitForm: function (event, autosubmit) { - var self = event.data.self; - var icinga = self.icinga; + var _this = event.data.self; + var icinga = _this.icinga; // .closest is not required unless subelements to trigger this var $form = $(event.currentTarget).closest('form'); var url = $form.attr('action'); @@ -217,6 +224,14 @@ var $target; var data; + var $rememberedSubmittButton = $form.data('submitButton'); + if (typeof $rememberedSubmittButton != 'undefined') { + if ($form.has($rememberedSubmittButton)) { + $button = $rememberedSubmittButton; + } + $form.removeData('submitButton'); + } + if ($button.length === 0) { var $el; @@ -262,9 +277,9 @@ $button.addClass('active'); } - $target = self.getLinkTargetFor($button); + $target = _this.getLinkTargetFor($button); } else { - $target = self.getLinkTargetFor($form); + $target = _this.getLinkTargetFor($form); } if (! url) { @@ -412,16 +427,16 @@ * Someone clicked a link or tr[href] */ linkClicked: function (event) { - var self = event.data.self; - var icinga = self.icinga; + var _this = event.data.self; + var icinga = _this.icinga; var $a = $(this); var $eventTarget = $(event.target); var href = $a.attr('href'); var linkTarget = $a.attr('target'); var $target; var formerUrl; - var remote = /^(?:[a-z]+:)\/\//; - if (href.match(/^(mailto|javascript|data):/)) { + if (href.match(/^(?:(?:mailto|javascript|data):|[a-z]+:\/\/)/)) { + event.stopPropagation(); return true; } @@ -439,10 +454,6 @@ } } - // Let remote links pass through - if (href.match(remote)) { - return true; - } // window.open is used as return true; didn't work reliable if (linkTarget === '_blank' || linkTarget === '_self') { window.open(href, linkTarget); @@ -501,7 +512,7 @@ } return false; } - $target = self.getLinkTargetFor($a); + $target = _this.getLinkTargetFor($a); formerUrl = $target.data('icingaUrl'); if (typeof formerUrl !== 'undefined' && formerUrl.split(/#/)[0] === href.split(/#/)[0]) { @@ -513,7 +524,7 @@ return false; } } else { - $target = self.getLinkTargetFor($a); + $target = _this.getLinkTargetFor($a); } // Load link URL @@ -566,11 +577,11 @@ } else if (targetId === '_main') { targetId = 'col1'; $target = $('#' + targetId); - self.icinga.ui.layout1col(); + this.icinga.ui.layout1col(); } else { $target = $('#' + targetId); if (! $target.length) { - self.icinga.logger.warn('Link target "#' + targetId + '" does not exist in DOM.'); + this.icinga.logger.warn('Link target "#' + targetId + '" does not exist in DOM.'); } } @@ -585,7 +596,7 @@ }, unbindGlobalHandlers: function () { - $.each(self.icinga.behaviors, function (name, behavior) { + $.each(this.icinga.behaviors, function (name, behavior) { behavior.unbind($(document)); }); $(window).off('resize', this.onWindowResize); diff --git a/public/js/icinga/history.js b/public/js/icinga/history.js index a0ebffa32..d0f6f6612 100644 --- a/public/js/icinga/history.js +++ b/public/js/icinga/history.js @@ -144,8 +144,8 @@ */ onHistoryChange: function (event) { - var self = event.data.self, - icinga = self.icinga; + var _this = event.data.self, + icinga = _this.icinga; icinga.logger.debug('Got a history change'); @@ -157,9 +157,9 @@ } // keep the last pushed url in sync with history changes - self.lastPushUrl = location.href; + _this.lastPushUrl = location.href; - self.applyLocationBar(); + _this.applyLocationBar(); // notify behaviors of the state change $.each(this.icinga.behaviors, function (i, behavior) { diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js index 2ff4d7f6f..45ea6e85e 100644 --- a/public/js/icinga/loader.js +++ b/public/js/icinga/loader.js @@ -114,13 +114,13 @@ contentType = false; } - var self = this; + var _this = this; var req = $.ajax({ type : method, url : url, data : data, headers: headers, - context: self, + context: _this, contentType: contentType, processData: ! isFormData }); @@ -153,9 +153,9 @@ * @param {object} $target The target container */ submitFormToIframe: function ($form, action, $target) { - var self = this; + var _this = this; - $form.prop('action', self.icinga.utils.addUrlParams(action, { + $form.prop('action', _this.icinga.utils.addUrlParams(action, { '_frameUpload': true })); $form.prop('target', 'fileupload-frame-target'); @@ -165,10 +165,10 @@ var $redirectMeta = $contents.find('meta[name="redirectUrl"]'); if ($redirectMeta.length) { - self.redirectToUrl($redirectMeta.attr('content'), $target); + _this.redirectToUrl($redirectMeta.attr('content'), $target); } else { // Fetch the frame's new content and paste it into the target - self.renderContentToContainer( + _this.renderContentToContainer( $contents.find('body').html(), $target, 'replace' @@ -208,16 +208,16 @@ }, autorefresh: function () { - var self = this; - if (self.autorefreshEnabled !== true) { + var _this = this; + if (_this.autorefreshEnabled !== true) { return; } $('.container').filter(this.filterAutorefreshingContainers).each(function (idx, el) { var $el = $(el); var id = $el.attr('id'); - if (typeof self.requests[id] !== 'undefined') { - self.icinga.logger.debug('No refresh, request pending for ', id); + if (typeof _this.requests[id] !== 'undefined') { + _this.icinga.logger.debug('No refresh, request pending for ', id); return; } @@ -225,12 +225,12 @@ var lastUpdate = $el.data('lastUpdate'); if (typeof interval === 'undefined' || ! interval) { - self.icinga.logger.info('No interval, setting default', id); + _this.icinga.logger.info('No interval, setting default', id); interval = 10; } if (typeof lastUpdate === 'undefined' || ! lastUpdate) { - self.icinga.logger.info('No lastUpdate, setting one', id); + _this.icinga.logger.info('No lastUpdate, setting one', id); $el.data('lastUpdate',(new Date()).getTime()); return; } @@ -248,12 +248,12 @@ return; } - if (self.loadUrl($el.data('icingaUrl'), $el, undefined, undefined, undefined, true) === false) { - self.icinga.logger.debug( + if (_this.loadUrl($el.data('icingaUrl'), $el, undefined, undefined, undefined, true) === false) { + _this.icinga.logger.debug( 'NOT autorefreshing ' + id + ', even if ' + interval + ' ms passed. Request pending?' ); } else { - self.icinga.logger.debug( + _this.icinga.logger.debug( 'Autorefreshing ' + id + ' ' + interval + ' ms passed' ); } @@ -277,12 +277,12 @@ processNotificationHeader: function(req) { var header = req.getResponseHeader('X-Icinga-Notification'); - var self = this; + var _this = this; if (! header) return false; var list = header.split('&'); $.each(list, function(idx, el) { var parts = decodeURIComponent(el).split(' '); - self.createNotice(parts.shift(), parts.join(' ')); + _this.createNotice(parts.shift(), parts.join(' ')); }); return true; }, @@ -406,15 +406,15 @@ // TODO: this is just a prototype, disabled for now return; - var self = this; + var _this = this; $('img.icon', $container).each(function(idx, img) { var src = $(img).attr('src'); - if (typeof self.iconCache[src] !== 'undefined') { + if (typeof _this.iconCache[src] !== 'undefined') { return; } var cache = new Image(); cache.src = src - self.iconCache[src] = cache; + _this.iconCache[src] = cache; }); }, @@ -422,7 +422,7 @@ * Handle successful XHR response */ onResponse: function (data, textStatus, req) { - var self = this; + var _this = this; if (this.failureNotice !== null) { if (! this.failureNotice.hasClass('fading-out')) { this.failureNotice.remove(); @@ -445,6 +445,10 @@ return; } + if (req.getResponseHeader('X-Icinga-Announcements') === 'refresh') { + _this.loadUrl(_this.url('/layout/announcements'), $('#announcements')); + } + // div helps getting an XML tree var $resp = $('
' + req.responseText + '
'); var active = false; @@ -540,7 +544,7 @@ var title = $('h1', $el).first(); $('h1', targets[i]).first().replaceWith(title); - self.loadUrl(url, targets[i]); + _this.loadUrl(url, targets[i]); i++; }); rendered = true; @@ -569,7 +573,7 @@ if (newBody) { this.icinga.ui.fixDebugVisibility().triggerWindowResize(); } - self.cacheLoadedIcons(req.$target); + _this.cacheLoadedIcons(req.$target); }, /** @@ -586,7 +590,7 @@ var url = req.url; if (req.$target[0].id === 'col1') { - self.icinga.behaviors.navigation.trySetActiveByUrl(url); + this.icinga.behaviors.navigation.trySetActiveByUrl(url); } var $forms = $('[action="' + this.icinga.utils.parseUrl(url).path + '"]'); @@ -735,7 +739,7 @@ renderContentToContainer: function (content, $container, action, autorefresh, forceFocus) { // Container update happens here var scrollPos = false; - var self = this; + var _this = this; var containerId = $container.attr('id'); var activeElementPath = false; @@ -772,7 +776,7 @@ $container.trigger('beforerender'); var discard = false; - $.each(self.icinga.behaviors, function(name, behavior) { + $.each(_this.icinga.behaviors, function(name, behavior) { if (behavior.renderHook) { var changed = behavior.renderHook(content, $container, action, autorefresh); if (!changed) { @@ -798,7 +802,7 @@ // }); $('.container', $container).each(function() { - self.stopPendingRequestsFor($(this)); + _this.stopPendingRequestsFor($(this)); }); if (false && @@ -827,7 +831,10 @@ if (typeof $container.attr('tabindex') === 'undefined') { $container.attr('tabindex', -1); } - $container.focus(); + // Do not touch focus in case a module or component already placed it + if ($(document.activeElement).closest('.container').attr('id') !== containerId) { + $container.focus(); + } }, 0); } } else { @@ -836,6 +843,15 @@ if ($activeElement.length && $activeElement.is(':visible')) { $activeElement.focus(); + if ($activeElement.is('input[type=text]')) { + if (typeof $activeElement[0].setSelectionRange === 'function') { + // Place focus after the last character. Could be extended to other + // input types, would require some \r\n "magic" to work around issues + // with some browsers + var len = $activeElement.val().length; + $activeElement[0].setSelectionRange(len, len); + } + } } else if (! autorefresh) { if (focusFallback) { $(focusFallback.parent).find(focusFallback.child).focus(); diff --git a/public/js/icinga/module.js b/public/js/icinga/module.js index 83700eb39..2c2368e5b 100644 --- a/public/js/icinga/module.js +++ b/public/js/icinga/module.js @@ -80,16 +80,16 @@ }, applyHandlers: function () { - var self = this; + var _this = this; $.each(this.registeredHandlers, function (key, on) { - self.bindEventHandler( + _this.bindEventHandler( on.event, on.filter, on.handler ); }); - self = null; + _this = null; return this; }, @@ -98,10 +98,10 @@ * Effectively bind the given event handler */ bindEventHandler: function (event, filter, handler) { - var self = this; + var _this = this; this.icinga.logger.debug('Bound ' + filter + ' .' + event + '()'); this.handlers.push([event, filter, handler]); - $(document).on(event, filter, handler.bind(self.object)); + $(document).on(event, filter, handler.bind(_this.object)); }, /** diff --git a/public/js/icinga/timer.js b/public/js/icinga/timer.js index 153e5325f..e16c5fe1d 100644 --- a/public/js/icinga/timer.js +++ b/public/js/icinga/timer.js @@ -49,8 +49,8 @@ * The initialization function starts our ticker */ initialize: function () { - var self = this; - this.ticker = setInterval(function () { self.tick(); }, this.interval); + var _this = this; + this.ticker = setInterval(function () { _this.tick(); }, this.interval); }, /** diff --git a/public/js/icinga/ui.js b/public/js/icinga/ui.js index af60a3ce9..484014af5 100644 --- a/public/js/icinga/ui.js +++ b/public/js/icinga/ui.js @@ -211,16 +211,16 @@ * Our window got resized, let's fix our UI */ onWindowResize: function (event) { - var self = event.data.self; + var _this = event.data.self; - if (self.layoutHasBeenChanged()) { - self.icinga.logger.info( + if (_this.layoutHasBeenChanged()) { + _this.icinga.logger.info( 'Layout change detected, switching to', - self.currentLayout + _this.currentLayout ); } - self.fixControls(); - self.refreshDebug(); + _this.fixControls(); + _this.refreshDebug(); }, /** @@ -458,7 +458,6 @@ * Initialize all TriStateCheckboxes in the given html */ initializeTriStates: function ($html) { - var self = this; $('div.tristate', $html).each(function(index, item) { var $target = $(item); @@ -658,14 +657,21 @@ $container.find('.controls').each(function() { var $controls = $(this); var $fakeControls = $controls.next('.fake-controls'); - $controls.css({ top: $container.offset().top, width: $fakeControls.outerWidth() }); - $fakeControls.height($controls.height()); }); + + var $statusBar = $container.children('.monitoring-statusbar'); + if ($statusBar.length) { + $statusBar.css({ + left: $container.offset().left, + width: $container.width() + }); + $statusBar.prev('.monitoring-statusbar-ghost').height($statusBar.outerHeight(true)); + } }, toggleFullscreen: function () { diff --git a/test/php/library/Icinga/Data/Filter/FilterTest.php b/test/php/library/Icinga/Data/Filter/FilterTest.php index 8b9336081..ef64b5a21 100644 --- a/test/php/library/Icinga/Data/Filter/FilterTest.php +++ b/test/php/library/Icinga/Data/Filter/FilterTest.php @@ -198,6 +198,20 @@ class FilterTest extends BaseTestCase $this->assertNotEquals((string) $c, (string) $d); } + public function testBooleanExpressionIsRenderedCorrectly() + { + $filter = Filter::fromQueryString('a&!b'); + $this->assertEquals( + $filter->toQueryString(), + 'a&!b' + ); + $this->assertEquals( + (string) $filter, + // TODO: I'd prefer to see 'a & !b' here: + 'a & (! b)' + ); + } + public function testLeadingAndTrailingWhitespaces() { $columnWithWhitespaces = Filter::where(' host ', 'localhost'); diff --git a/test/php/library/Icinga/File/CsvTest.php b/test/php/library/Icinga/File/CsvTest.php index 6eaf6bad0..048e3b073 100644 --- a/test/php/library/Icinga/File/CsvTest.php +++ b/test/php/library/Icinga/File/CsvTest.php @@ -3,7 +3,7 @@ namespace Tests\Icinga\File; -use Mockery; +use Icinga\Data\DataArray\ArrayDatasource; use Icinga\File\Csv; use Icinga\Test\BaseTestCase; @@ -11,19 +11,15 @@ class CsvTest extends BaseTestCase { public function testWhetherValidCsvIsRendered() { - $queryMock = Mockery::mock( - 'Icinga\Data\SimpleQuery', - array( - 'fetchAll' => array( - array('col1' => 'val1', 'col2' => 'val2', 'col3' => 'val3', 'col4' => 'val4'), - array('col1' => 'val5', 'col2' => 'val6', 'col3' => 'val7', 'col4' => 'val8') - ) - ) - ); - $csv = Csv::fromQuery($queryMock); + $data = new ArrayDatasource(array( + array('col1' => 'val1', 'col2' => 'val2', 'col3' => 'val3', 'col4' => 'val4'), + array('col1' => 'val5', 'col2' => 'val6', 'col3' => 'val7', 'col4' => 'val8') + )); + + $csv = Csv::fromQuery($data->select()); $this->assertEquals( - join( + implode( "\r\n", array( 'col1,col2,col3,col4', diff --git a/test/php/library/Icinga/File/Ini/IniWriterTest.php b/test/php/library/Icinga/File/Ini/IniWriterTest.php index 3148779ef..9e38aa9bd 100644 --- a/test/php/library/Icinga/File/Ini/IniWriterTest.php +++ b/test/php/library/Icinga/File/Ini/IniWriterTest.php @@ -400,4 +400,53 @@ EOD; file_put_contents($this->tempFile, $config); return $this->tempFile; } + + public function testWhetherNullValuesGetPersisted() + { + $config = Config::fromArray(array()); + $section = $config->getSection('garbage'); + $section->foobar = null; + $config->setSection('garbage', $section); + + $iniWriter = new IniWriter($config, '/dev/null'); + $this->assertNotContains( + 'foobar', + $iniWriter->render(), + 'IniWriter persists section keys with null values' + ); + } + + public function testWhetherEmptyValuesGetPersisted() + { + $config = Config::fromArray(array()); + $section = $config->getSection('garbage'); + $section->foobar = ''; + $config->setSection('garbage', $section); + + $iniWriter = new IniWriter($config, '/dev/null'); + $this->assertContains( + 'foobar', + $iniWriter->render(), + 'IniWriter doesn\'t persist section keys with empty values' + ); + } + + public function testExplicitRemove() + { + $filename = tempnam(sys_get_temp_dir(), 'iw2'); + $config = Config::fromArray(array('garbage' => array('foobar' => 'lolcat'))); + $iniWriter = new IniWriter($config, $filename); + $iniWriter->write(); + + $section = $config->getSection('garbage'); + $section->foobar = null; + $iniWriter = new IniWriter($config, $filename); + $this->assertNotContains( + 'foobar', + $iniWriter->render(), + 'IniWriter doesn\'t remove section keys with null values' + ); + + unlink($filename); + } } diff --git a/test/php/library/Icinga/Web/UrlTest.php b/test/php/library/Icinga/Web/UrlTest.php index 0481d723b..4975199e6 100644 --- a/test/php/library/Icinga/Web/UrlTest.php +++ b/test/php/library/Icinga/Web/UrlTest.php @@ -9,6 +9,78 @@ use Icinga\Test\BaseTestCase; class UrlTest extends BaseTestCase { + public function testWhetherFromPathCutsOfTheFirstCharOfThePathIfUrlIsInternalAndHasAUsernameInIt() + { + $this->getRequestMock()->shouldReceive('getServer')->with("SERVER_NAME")->andReturn('localhost') + ->shouldReceive('getServer')->with("SERVER_PORT")->andReturn('8080'); + + $url = Url::fromPath('http://testusername:testpassword@localhost:8080/path/to/my/url.html'); + $this->assertEquals( + 'path/to/my/url.html', + $url->getPath(), + 'Url::fromPath does not cut of the first char of path if the url is internal and has a username in it' + ); + } + + public function testWhetherGetAbsoluteUrlReturnsTheBasePathIfUrlIsInternalAndHasAUsernameInIt() + { + $this->getRequestMock()->shouldReceive('getServer')->with("SERVER_NAME")->andReturn('localhost') + ->shouldReceive('getServer')->with("SERVER_PORT")->andReturn('8080'); + + $url = Url::fromPath('http://testusername:testpassword@localhost:8080/path/to/my/url.html'); + $this->assertEquals( + 'http://testusername:testpassword@localhost:8080/path/to/my/url.html', + $url->getAbsoluteUrl(), + 'Url::getAbsoluteUrl does not reassemble the correct basePath' + ); + } + + public function testWhetherGetAbsoluteUrlReturnsTheBasePathIfUrlIsInternalAndHasNoUsernameInIt() + { + $url = Url::fromPath('/path/to/my/url.html'); + $this->assertEquals( + '/path/to/my/url.html', + $url->getAbsoluteUrl(), + 'Url::getAbsoluteUrl does not reassemble the correct basePath' + ); + } + + public function testWhetherGetAbsoluteUrlReturnsTheBasePathIfUrlIsExternalAndHasAUsernameInIt() + { + $this->getRequestMock()->shouldReceive('getServer')->with("SERVER_NAME")->andReturn('localhost') + ->shouldReceive('getServer')->with("SERVER_PORT")->andReturn('8080'); + + $url = Url::fromPath('http://testusername:testpassword@testhost/path/to/my/url.html'); + $this->assertEquals( + 'http://testusername:testpassword@testhost/path/to/my/url.html', + $url->getAbsoluteUrl(), + 'Url::getAbsoluteUrl does not reassemble the correct basePath' + ); + } + + public function testWhetherGetAbsoluteUrlReturnsTheBasePathIfUrlIsExternalAndHasNoUsernameInIt() + { + $this->getRequestMock()->shouldReceive('getServer')->with("SERVER_NAME")->andReturn('localhost') + ->shouldReceive('getServer')->with("SERVER_PORT")->andReturn('8080'); + + $url = Url::fromPath('http://testhost/path/to/my/url.html'); + $this->assertEquals( + 'http://testhost/path/to/my/url.html', + $url->getAbsoluteUrl(), + 'Url::getAbsoluteUrl does not reassemble the correct basePath' + ); + } + + public function testWhetherGetAbsoluteUrlReturnsTheGivenUsernameAndPassword() + { + $url = Url::fromPath('http://testusername:testpassword@testsite.com/path/to/my/url.html'); + $this->assertEquals( + 'http://testusername:testpassword@testsite.com/path/to/my/url.html', + $url->getAbsoluteUrl(), + 'Url::fromPath does not reassemble the correct url' + ); + } + public function testWhetherFromRequestWorksWithoutARequest() { $this->getRequestMock()->shouldReceive('getBaseUrl')->andReturn('/path/to') @@ -83,8 +155,8 @@ class UrlTest extends BaseTestCase $this->assertEquals( '', - $url->getBaseUrl(), - 'Url::fromPath does not recognize the correct base url' + $url->getBasePath(), + 'Url::fromPath does not recognize the correct base path' ); $this->assertEquals( '/my/test/url.html', diff --git a/test/php/regression/Bug11831Test.php b/test/php/regression/Bug11831Test.php new file mode 100644 index 000000000..9264c46f9 --- /dev/null +++ b/test/php/regression/Bug11831Test.php @@ -0,0 +1,37 @@ +getProperty('metadataFile'); + $prop->setAccessible(true); + $prop->setValue($module, stream_get_meta_data($moduleInfoFile)['uri']); + $this->assertEquals($module->getVersion(), '1.0.0'); + fclose($moduleInfoFile); + } +}