/epub/PHPePub/EPub.NCX.php
PHP | 782 lines | 412 code | 85 blank | 285 comment | 67 complexity | 5d0f3ddee2b54109b4f91f63fe5211a8 MD5 | raw file
- <?php
- /**
- * ePub NCX file structure
- *
- * @author A. Grandt <php@grandt.com>
- * @copyright 2009-2014 A. Grandt
- * @license GNU LGPL, Attribution required for commercial implementations, requested for everything else.
- * @version 3.20
- */
- class Ncx {
- const _VERSION = 3.20;
- const MIMETYPE = "application/x-dtbncx+xml";
- private $bookVersion = EPub::BOOK_VERSION_EPUB2;
- private $navMap = NULL;
- private $uid = NULL;
- private $meta = array();
- private $docTitle = NULL;
- private $docAuthor = NULL;
- private $currentLevel = NULL;
- private $lastLevel = NULL;
- private $languageCode = "en";
- private $writingDirection = EPub::DIRECTION_LEFT_TO_RIGHT;
- public $chapterList = array();
- public $referencesTitle = "Guide";
- public $referencesClass = "references";
- public $referencesId = "references";
- public $referencesList = array();
- public $referencesName = array();
- public $referencesOrder = NULL;
- /**
- * Class constructor.
- *
- * @param string $uid
- * @param string $docTitle
- * @param string $docAuthor
- * @param string $languageCode
- * @param string $writingDirection
- */
- function __construct($uid = NULL, $docTitle = NULL, $docAuthor = NULL, $languageCode = "en", $writingDirection = EPub::DIRECTION_LEFT_TO_RIGHT) {
- $this->navMap = new NavMap($writingDirection);
- $this->currentLevel = $this->navMap;
- $this->setUid($uid);
- $this->setDocTitle($docTitle);
- $this->setDocAuthor($docAuthor);
- $this->setLanguageCode($languageCode);
- $this->setWritingDirection($writingDirection);
- }
- /**
- * Class destructor
- *
- * @return void
- */
- function __destruct() {
- unset($this->bookVersion, $this->navMap, $this->uid, $this->meta);
- unset($this->docTitle, $this->docAuthor, $this->currentLevel, $this->lastLevel);
- unset($this->languageCode, $this->writingDirection, $this->chapterList, $this->referencesTitle);
- unset($this->referencesClass, $this->referencesId, $this->referencesList, $this->referencesName);
- unset($this->referencesOrder);
- }
- /**
- *
- * Enter description here ...
- *
- * @param string $bookVersion
- */
- function setVersion($bookVersion) {
- $this->bookVersion = is_string($bookVersion) ? trim($bookVersion) : EPub::BOOK_VERSION_EPUB2;
- }
- /**
- *
- * @return bool TRUE if the book is set to type ePub 2
- */
- function isEPubVersion2() {
- return $this->bookVersion === EPub::BOOK_VERSION_EPUB2;
- }
- /**
- *
- * Enter description here ...
- *
- * @param string $uid
- */
- function setUid($uid) {
- $this->uid = is_string($uid) ? trim($uid) : NULL;
- }
- /**
- *
- * Enter description here ...
- *
- * @param string $docTitle
- */
- function setDocTitle($docTitle) {
- $this->docTitle = is_string($docTitle) ? trim($docTitle) : NULL;
- }
- /**
- *
- * Enter description here ...
- *
- * @param string $docAuthor
- */
- function setDocAuthor($docAuthor) {
- $this->docAuthor = is_string($docAuthor) ? trim($docAuthor) : NULL;
- }
- /**
- *
- * Enter description here ...
- *
- * @param string $languageCode
- */
- function setLanguageCode($languageCode) {
- $this->languageCode = is_string($languageCode) ? trim($languageCode) : "en";
- }
- /**
- *
- * Enter description here ...
- *
- * @param string $writingDirection
- */
- function setWritingDirection($writingDirection) {
- $this->writingDirection = is_string($writingDirection) ? trim($writingDirection) : EPub::DIRECTION_LEFT_TO_RIGHT;
- }
- /**
- *
- * Enter description here ...
- *
- * @param NavMap $navMap
- */
- function setNavMap($navMap) {
- if ($navMap != NULL && is_object($navMap) && get_class($navMap) === "NavMap") {
- $this->navMap = $navMap;
- }
- }
- /**
- * Add one chapter level.
- *
- * Subsequent chapters will be added to this level.
- *
- * @param string $navTitle
- * @param string $navId
- * @param string $navClass
- * @param string $isNavHidden
- * @param string $writingDirection
- * @return NavPoint
- */
- function subLevel($navTitle = NULL, $navId = NULL, $navClass = NULL, $isNavHidden = FALSE, $writingDirection = NULL) {
- $navPoint = FALSE;
- if (isset($navTitle) && isset($navClass)) {
- $navPoint = new NavPoint($navTitle, NULL, $navId, $navClass, $isNavHidden, $writingDirection);
- $this->addNavPoint($navPoint);
- }
- if ($this->lastLevel !== NULL) {
- $this->currentLevel = $this->lastLevel;
- }
- return $navPoint;
- }
- /**
- * Step back one chapter level.
- *
- * Subsequent chapters will be added to this chapters parent level.
- */
- function backLevel() {
- $this->lastLevel = $this->currentLevel;
- $this->currentLevel = $this->currentLevel->getParent();
- }
- /**
- * Step back to the root level.
- *
- * Subsequent chapters will be added to the rooot NavMap.
- */
- function rootLevel() {
- $this->lastLevel = $this->currentLevel;
- $this->currentLevel = $this->navMap;
- }
- /**
- * Step back to the given level.
- * Useful for returning to a previous level from deep within the structure.
- * Values below 2 will have the same effect as rootLevel()
- *
- * @param int $newLevel
- */
- function setCurrentLevel($newLevel) {
- if ($newLevel <= 1) {
- $this->rootLevel();
- } else {
- while ($this->currentLevel->getLevel() > $newLevel) {
- $this->backLevel();
- }
- }
- }
- /**
- * Get current level count.
- * The indentation of the current structure point.
- *
- * @return current level count;
- */
- function getCurrentLevel() {
- return $this->currentLevel->getLevel();
- }
- /**
- * Add child NavPoints to current level.
- *
- * @param NavPoint $navPoint
- */
- function addNavPoint($navPoint) {
- $this->lastLevel = $this->currentLevel->addNavPoint($navPoint);
- }
- /**
- *
- * Enter description here ...
- *
- * @return NavMap
- */
- function getNavMap() {
- return $this->navMap;
- }
- /**
- *
- * Enter description here ...
- *
- * @param string $name
- * @param string $content
- */
- function addMetaEntry($name, $content) {
- $name = is_string($name) ? trim($name) : NULL;
- $content = is_string($content) ? trim($content) : NULL;
- if ($name != NULL && $content != NULL) {
- $this->meta[] = array($name => $content);
- }
- }
- /**
- *
- * Enter description here ...
- *
- * @return string
- */
- function finalize() {
- $nav = $this->navMap->finalize();
- $ncx = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
- if ($this->isEPubVersion2()) {
- $ncx .= "<!DOCTYPE ncx PUBLIC \"-//NISO//DTD ncx 2005-1//EN\"\n"
- . " \"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd\">\n";
- }
- $ncx .= "<ncx xmlns=\"http://www.daisy.org/z3986/2005/ncx/\" version=\"2005-1\" xml:lang=\"" . $this->languageCode . "\" dir=\"" . $this->writingDirection . "\">\n"
- . "\t<head>\n"
- . "\t\t<meta name=\"dtb:uid\" content=\"" . $this->uid . "\" />\n"
- . "\t\t<meta name=\"dtb:depth\" content=\"" . $this->navMap->getNavLevels() . "\" />\n"
- . "\t\t<meta name=\"dtb:totalPageCount\" content=\"0\" />\n"
- . "\t\t<meta name=\"dtb:maxPageNumber\" content=\"0\" />\n";
- if (sizeof($this->meta)) {
- foreach ($this->meta as $metaEntry) {
- list($name, $content) = each($metaEntry);
- $ncx .= "\t\t<meta name=\"" . $name . "\" content=\"" . $content . "\" />\n";
- }
- }
- $ncx .= "\t</head>\n\n\t<docTitle>\n\t\t<text>"
- . $this->docTitle
- . "</text>\n\t</docTitle>\n\n\t<docAuthor>\n\t\t<text>"
- . $this->docAuthor
- . "</text>\n\t</docAuthor>\n\n"
- . $nav;
- return $ncx . "</ncx>\n";
- }
- /**
- *
- * @param string $title
- * @param string $cssFileName
- * @return string
- */
- function finalizeEPub3($title = "Table of Contents", $cssFileName = NULL) {
- $end = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- . "<html xmlns=\"http://www.w3.org/1999/xhtml\"\n"
- . " xmlns:epub=\"http://www.idpf.org/2007/ops\"\n"
- . " xml:lang=\"" . $this->languageCode . "\" lang=\"" . $this->languageCode . "\" dir=\"" . $this->writingDirection . "\">\n"
- . "\t<head>\n"
- . "\t\t<title>" . $this->docTitle . "</title>\n"
- . "\t\t<meta http-equiv=\"default-style\" content=\"text/html; charset=utf-8\"/>\n";
- if ($cssFileName !== NULL) {
- $end .= "\t\t<link rel=\"stylesheet\" href=\"" . $cssFileName . "\" type=\"text/css\"/>\n";
- }
- $end .= "\t</head>\n"
- . "\t<body epub:type=\"frontmatter toc\">\n"
- . "\t\t<header>\n"
- . "\t\t\t<h1>" . $title . "</h1>\n"
- . "\t\t</header>\n"
- . $this->navMap->finalizeEPub3()
- . $this->finalizeEPub3Landmarks()
- . "\t</body>\n"
- . "</html>\n";
- return $end;
- }
- /**
- * Build the references for the ePub 2 toc.
- * These are merely reference pages added to the end of the navMap though.
- *
- * @return string
- */
- function finalizeReferences() {
- if (isset($this->referencesList) && sizeof($this->referencesList) > 0) {
- $this->rootLevel();
- $this->subLevel($this->referencesTitle, $this->referencesId, $this->referencesClass);
- $refId = 1;
- while (list($item, $descriptive) = each($this->referencesOrder)) {
- if (array_key_exists($item, $this->referencesList)) {
- $name = (empty($this->referencesName[$item]) ? $descriptive : $this->referencesName[$item]);
- $navPoint = new NavPoint($name, $this->referencesList[$item], "ref-" . $refId++);
- $this->addNavPoint($navPoint);
- }
- }
- }
- }
- /**
- * Build the landmarks for the ePub 3 toc.
- * @return string
- */
- function finalizeEPub3Landmarks() {
- $lm = "";
- if (isset($this->referencesList) && sizeof($this->referencesList) > 0) {
- $lm = "\t\t\t<nav epub:type=\"landmarks\">\n"
- . "\t\t\t\t<h2"
- . ($this->writingDirection === EPub::DIRECTION_RIGHT_TO_LEFT ? " dir=\"rtl\"" : "")
- . ">" . $this->referencesTitle . "</h2>\n"
- . "\t\t\t\t<ol>\n";
- $li = "";
- while (list($item, $descriptive) = each($this->referencesOrder)) {
- if (array_key_exists($item, $this->referencesList)) {
- $li .= "\t\t\t\t\t<li><a epub:type=\""
- . $item
- . "\" href=\"" . $this->referencesList[$item] . "\">"
- . (empty($this->referencesName[$item]) ? $descriptive : $this->referencesName[$item])
- . "</a></li>\n";
- }
- }
- if (empty($li)) {
- return "";
- }
- $lm .= $li
- . "\t\t\t\t</ol>\n"
- . "\t\t\t</nav>\n";
- }
- return $lm;
- }
- }
- /**
- * ePub NavMap class
- */
- class NavMap {
- const _VERSION = 3.00;
- private $navPoints = array();
- private $navLevels = 0;
- private $writingDirection = NULL;
- /**
- * Class constructor.
- *
- * @return void
- */
- function __construct($writingDirection = NULL) {
- $this->setWritingDirection($writingDirection);
- }
- /**
- * Class destructor
- *
- * @return void
- */
- function __destruct() {
- unset($this->navPoints, $this->navLevels, $this->writingDirection);
- }
- /**
- * Set the writing direction to be used for this NavPoint.
- *
- * @param string $writingDirection
- */
- function setWritingDirection($writingDirection) {
- $this->writingDirection = isset($writingDirection) && is_string($writingDirection) ? trim($writingDirection) : NULL;
- }
- function getWritingDirection() {
- return $this->writingDirection;
- }
- /**
- * Add a navPoint to the root of the NavMap.
- *
- * @param NavPoint $navPoint
- * @return NavMap
- */
- function addNavPoint($navPoint) {
- if ($navPoint != NULL && is_object($navPoint) && get_class($navPoint) === "NavPoint") {
- $navPoint->setParent($this);
- if ($navPoint->getWritingDirection() == NULL) {
- $navPoint->setWritingDirection($this->writingDirection);
- }
- $this->navPoints[] = $navPoint;
- return $navPoint;
- }
- return $this;
- }
- /**
- * The final max depth for the "dtb:depth" meta attribute
- * Only available after finalize have been called.
- *
- * @return number
- */
- function getNavLevels() {
- return $this->navLevels+1;
- }
- function getLevel() {
- return 1;
- }
- function getParent() {
- return $this;
- }
- /**
- * Finalize the navMap, the final max depth for the "dtb:depth" meta attribute can be retrieved with getNavLevels after finalization
- *
- */
- function finalize() {
- $playOrder = 0;
- $this->navLevels = 0;
- $nav = "\t<navMap>\n";
- if (sizeof($this->navPoints) > 0) {
- $this->navLevels++;
- foreach ($this->navPoints as $navPoint) {
- $retLevel = $navPoint->finalize($nav, $playOrder, 0);
- if ($retLevel > $this->navLevels) {
- $this->navLevels = $retLevel;
- }
- }
- }
- return $nav . "\t</navMap>\n";
- }
- /**
- * Finalize the navMap, the final max depth for the "dtb:depth" meta attribute can be retrieved with getNavLevels after finalization
- *
- */
- function finalizeEPub3() {
- $playOrder = 0;
- $level = 0;
- $this->navLevels = 0;
- $nav = "\t\t<nav epub:type=\"toc\" id=\"toc\">\n";
- if (sizeof($this->navPoints) > 0) {
- $this->navLevels++;
- $nav .= str_repeat("\t", $level) . "\t\t\t<ol epub:type=\"list\">\n";
- foreach ($this->navPoints as $navPoint) {
- $retLevel = $navPoint->finalizeEPub3($nav, $playOrder, 0);
- if ($retLevel > $this->navLevels) {
- $this->navLevels = $retLevel;
- }
- }
- $nav .= str_repeat("\t", $level) . "\t\t\t</ol>\n";
- }
- return $nav . "\t\t</nav>\n";
- }
- }
- /**
- * ePub NavPoint class
- */
- class NavPoint {
- const _VERSION = 3.00;
- private $label = NULL;
- private $contentSrc = NULL;
- private $id = NULL;
- private $navClass = NULL;
- private $isNavHidden = FALSE;
- private $navPoints = array();
- private $parent = NULL;
- /**
- * Class constructor.
- *
- * All three attributes are mandatory, though if ID is set to null (default) the value will be generated.
- *
- * @param string $label
- * @param string $contentSrc
- * @param string $id
- * @param string $navClass
- * @param bool $isNavHidden
- * @param string $writingDirection
- */
- function __construct($label, $contentSrc = NULL, $id = NULL, $navClass = NULL, $isNavHidden = FALSE, $writingDirection = NULL) {
- $this->setLabel($label);
- $this->setContentSrc($contentSrc);
- $this->setId($id);
- $this->setNavClass($navClass);
- $this->setNavHidden($isNavHidden);
- $this->setWritingDirection($writingDirection);
- }
- /**
- * Class destructor
- *
- * @return void
- */
- function __destruct() {
- unset($this->label, $this->contentSrc, $this->id, $this->navClass);
- unset($this->isNavHidden, $this->navPoints, $this->parent);
- }
- /**
- * Set the Text label for the NavPoint.
- *
- * The label is mandatory.
- *
- * @param string $label
- */
- function setLabel($label) {
- $this->label = is_string($label) ? trim($label) : NULL;
- }
- /**
- * Get the Text label for the NavPoint.
- *
- * @return string Label
- */
- function getLabel() {
- return $this->label;
- }
- /**
- * Set the src reference for the NavPoint.
- *
- * The src is mandatory for ePub 2.
- *
- * @param string $contentSrc
- */
- function setContentSrc($contentSrc) {
- $this->contentSrc = isset($contentSrc) && is_string($contentSrc) ? trim($contentSrc) : NULL;
- }
- /**
- * Get the src reference for the NavPoint.
- *
- * @return string content src url.
- */
- function getContentSrc() {
- return $this->contentSrc;
- }
- /**
- * Set the parent for this NavPoint.
- *
- * @param NavPoint or NavMap $parent
- */
- function setParent($parent) {
- if ($parent != NULL && is_object($parent) &&
- (get_class($parent) === "NavPoint" || get_class($parent) === "NavMap") ) {
- $this->parent = $parent;
- }
- }
- /**
- * Get the parent to this NavPoint.
- *
- * @return NavPoint, or NavMap if the parent is the root.
- */
- function getParent() {
- return $this->parent;
- }
- /**
- * Get the current level. 1 = document root.
- *
- * @return int level
- */
- function getLevel() {
- return $this->parent === NULL ? 1 : $this->parent->getLevel()+1;
- }
- /**
- * Set the id for the NavPoint.
- *
- * The id must be unique, and is mandatory.
- *
- * @param string $id
- */
- function setId($id) {
- $this->id = is_string($id) ? trim($id) : NULL;
- }
- /**
- * Set the class to be used for this NavPoint.
- *
- * @param string $navClass
- */
- function setNavClass($navClass) {
- $this->navClass = isset($navClass) && is_string($navClass) ? trim($navClass) : NULL;
- }
- /**
- * Set the class to be used for this NavPoint.
- *
- * @param string $navClass
- */
- function setNavHidden($isNavHidden) {
- $this->isNavHidden = $isNavHidden === TRUE;
- }
- /**
- * Set the writing direction to be used for this NavPoint.
- *
- * @param string $writingDirection
- */
- function setWritingDirection($writingDirection) {
- $this->writingDirection = isset($writingDirection) && is_string($writingDirection) ? trim($writingDirection) : NULL;
- }
- function getWritingDirection() {
- return $this->writingDirection;
- }
- /**
- * Add child NavPoints for multi level NavMaps.
- *
- * @param NavPoint $navPoint
- */
- function addNavPoint($navPoint) {
- if ($navPoint != NULL && is_object($navPoint) && get_class($navPoint) === "NavPoint") {
- $navPoint->setParent($this);
- if ($navPoint->getWritingDirection() == NULL) {
- $navPoint->setWritingDirection($this->writingDirection);
- }
- $this->navPoints[] = $navPoint;
- return $navPoint;
- }
- return $this;
- }
- /**
- *
- * Enter description here ...
- *
- * @param string $nav
- * @param int $playOrder
- * @param int $level
- * @return int
- */
- function finalize(&$nav = "", &$playOrder = 0, $level = 0) {
- $maxLevel = $level;
- $levelAdjust = 0;
- if ($this->isNavHidden) {
- return $maxLevel;
- }
- if (isset($this->contentSrc)) {
- $playOrder++;
- if ($this->id == NULL) {
- $this->id = "navpoint-" . $playOrder;
- }
- $nav .= str_repeat("\t", $level) . "\t\t<navPoint id=\"" . $this->id . "\" playOrder=\"" . $playOrder . "\">\n"
- . str_repeat("\t", $level) . "\t\t\t<navLabel>\n"
- . str_repeat("\t", $level) . "\t\t\t\t<text>" . $this->label . "</text>\n"
- . str_repeat("\t", $level) . "\t\t\t</navLabel>\n"
- . str_repeat("\t", $level) . "\t\t\t<content src=\"" . $this->contentSrc . "\" />\n";
- } else {
- $levelAdjust++;
- }
- if (sizeof($this->navPoints) > 0) {
- $maxLevel++;
- foreach ($this->navPoints as $navPoint) {
- $retLevel = $navPoint->finalize($nav, $playOrder, ($level+1+$levelAdjust));
- if ($retLevel > $maxLevel) {
- $maxLevel = $retLevel;
- }
- }
- }
- if (isset($this->contentSrc)) {
- $nav .= str_repeat("\t", $level) . "\t\t</navPoint>\n";
- }
- return $maxLevel;
- }
- /**
- *
- * Enter description here ...
- *
- * @param string $nav
- * @param int $playOrder
- * @param int $level
- * @return int
- */
- function finalizeEPub3(&$nav = "", &$playOrder = 0, $level = 0, $subLevelClass = NULL, $subLevelHidden = FALSE) {
- $maxLevel = $level;
- if ($this->id == NULL) {
- $this->id = "navpoint-" . $playOrder;
- }
- $indent = str_repeat("\t", $level) . "\t\t\t\t";
- $nav .= $indent . "<li id=\"" . $this->id . "\"";
- if (isset($this->writingDirection)) {
- $nav .= " dir=\"" . $this->writingDirection . "\"";
- }
- $nav .= ">\n";
- if (isset($this->contentSrc)) {
- $nav .= $indent . "\t<a href=\"" . $this->contentSrc . "\">" . $this->label . "</a>\n";
- } else {
- $nav .= $indent . "\t<span>" . $this->label . "</span>\n";
- }
- if (sizeof($this->navPoints) > 0) {
- $maxLevel++;
- $nav .= $indent . "\t<ol epub:type=\"list\"";
- if (isset($subLevelClass)) {
- $nav .= " class=\"" . $subLevelClass . "\"";
- }
- if ($subLevelHidden) {
- $nav .= " hidden=\"hidden\"";
- }
- $nav .= ">\n";
- foreach ($this->navPoints as $navPoint) {
- $retLevel = $navPoint->finalizeEPub3($nav, $playOrder, ($level+2), $subLevelClass, $subLevelHidden);
- if ($retLevel > $maxLevel) {
- $maxLevel = $retLevel;
- }
- }
- $nav .= $indent . "\t</ol>\n";
- }
- $nav .= $indent . "</li>\n";
- return $maxLevel;
- }
- }
- ?>