From 945db4d511595bebe6312631fc5390b1d20f02f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Grandt?= Date: Sun, 21 Feb 2016 13:32:51 +0100 Subject: [PATCH] referenced links (url#id) broke on split chapters Fixed: referenced links (url#id) broke on split chapters. Changed: Generated TOC file changed from using hardcoded spaces to indent nested chapters, to using the CSS, defaulting to 2em per level. The tocCss can override this by defining .level[1-n], though the default only defines indents for levels 1-7. Reference links has their class as class=".level1 reference" --- REVISION.TXT | 12 ++- src/PHPePub/Core/EPub.php | 90 +++++++++++++++++++-- src/PHPePub/Core/Structure/OPF/Item.php | 38 +++++++++ src/PHPePub/Core/Structure/OPF/Manifest.php | 7 ++ src/PHPePub/Core/Structure/Opf.php | 39 +++++++++ 5 files changed, 175 insertions(+), 11 deletions(-) diff --git a/REVISION.TXT b/REVISION.TXT index 83b86a9..dba0fcc 100644 --- a/REVISION.TXT +++ b/REVISION.TXT @@ -1,9 +1,13 @@ --------------------------------------------------------------------- Rev. 4.0.6 - 2016-02-21 -* Added iBooksHelper -* Added CalibreHelper -* Added preliminary work for "Rendition" meta data -* Updated J. King's DrUUID library +* Added: iBooksHelper +* Added: CalibreHelper +* Added: preliminary work for "Rendition" meta data +* Fixed: referenced links (url#id) broke on split chapters. +* Changed: Generated TOC file changed from using hardcoded spaces to indent nested chapters, to using the CSS, defaulting to 2em per level. +* the tocCss can override this by defining .level[1-n], though the default only defines indents for levels 1-7. +* reference links has their class as class=".level1 reference" +* Updated: J. King's DrUUID library --------------------------------------------------------------------- Rev. 4.0.5 - 2015-11-02 * Added: Support for SVG images. (experimental) diff --git a/src/PHPePub/Core/EPub.php b/src/PHPePub/Core/EPub.php index 28d0d1c..98cb6d4 100644 --- a/src/PHPePub/Core/EPub.php +++ b/src/PHPePub/Core/EPub.php @@ -3,10 +3,12 @@ use com\grandt\BinStringStatic; use DOMDocument; +use DOMXPath; use PHPePub\Core\Structure\Ncx; use PHPePub\Core\Structure\NCX\NavPoint; use PHPePub\Core\Structure\Opf; use PHPePub\Core\Structure\OPF\DublinCore; +use PHPePub\Core\Structure\OPF\Item; use PHPePub\Core\Structure\OPF\MarcCode; use PHPePub\Core\Structure\OPF\MetaValue; use PHPePub\Core\Structure\OPF\Reference; @@ -266,6 +268,8 @@ function addChapter($chapterName, $fileName, $chapterData = null, $autoSplit = f $this->chapterCount++; $this->addFile($fileName, "chapter" . $this->chapterCount, $chapter, "application/xhtml+xml"); + $this->extractIdAttributes("chapter" . $this->chapterCount, $chapter); + $this->opf->addItemRef("chapter" . $this->chapterCount); $navPoint = new NavPoint(StringHelper::decodeHtmlEntities($chapterName), $fileName, "chapter" . $this->chapterCount); @@ -280,8 +284,6 @@ function addChapter($chapterName, $fileName, $chapterData = null, $autoSplit = f $partCount = 0; $this->chapterCount++; - $this->log->logLine("addChapter: \$chapterCount: " . $this->chapterCount); - $oneChapter = each($chapter); while ($oneChapter) { /** @noinspection PhpUnusedLocalVariableInspection */ @@ -296,12 +298,15 @@ function addChapter($chapterName, $fileName, $chapterData = null, $autoSplit = f $partCount++; $partName = $name . "_" . $partCount; $this->addFile($partName . "." . $extension, $partName, $v, "application/xhtml+xml"); + $this->extractIdAttributes($partName, $v); + $this->opf->addItemRef($partName); $oneChapter = each($chapter); } $partName = $name . "_1." . $extension; $navPoint = new NavPoint(StringHelper::decodeHtmlEntities($chapterName), $partName, $partName); + $this->ncx->addNavPoint($navPoint); $this->ncx->chapterList[$chapterName] = $navPoint; @@ -309,6 +314,27 @@ function addChapter($chapterName, $fileName, $chapterData = null, $autoSplit = f $this->chapterCount++; //$this->opf->addItemRef("chapter" . $this->chapterCount); + $id = preg_split("/[#]/", $fileName); + if (sizeof($id) == 2 && $this->isLogging) { + + $name = preg_split('/[\.]/', $id[0]); + if (sizeof($name) > 1) { + $name = $name[0]; + } + + $rv = $this->opf->getItemByHref($name, true); + + if ($rv != false) { + /** @var Item $item */ + foreach($rv as $item) { + if ($item->hasIndexPoint($id[1])) { + $fileName = $item->getHref() . "#" . $id[1]; + break; + } + } + } + } + $navPoint = new NavPoint(StringHelper::decodeHtmlEntities($chapterName), $fileName, "chapter" . $this->chapterCount); $this->ncx->addNavPoint($navPoint); $this->ncx->chapterList[$chapterName] = $navPoint; @@ -325,6 +351,39 @@ function addChapter($chapterName, $fileName, $chapterData = null, $autoSplit = f return $navPoint; } + /** + * find all id attributes in the html document. + * + * @param string $chapterData + * @return array + */ + function findIdAttributes($chapterData) { + $xmlDoc = new DOMDocument(); + @$xmlDoc->loadHTML($chapterData); + + $xpath = new DomXpath($xmlDoc); + + $rv = array(); + // traverse all results + foreach ($xpath->query('//@id') as $rowNode) { + $rv[] = $rowNode->nodeValue; + } + + return $rv; + } + + /** + * @param string $partName + * @param string $chapterData + */ + public function extractIdAttributes($partName, $chapterData) { + $item = $this->opf->getItemById($partName); + $ids = $this->findIdAttributes($chapterData); + foreach ($ids as $id) { + $item->addIndexPoint($id); + } + } + /** * Process external references from a HTML to the book. The chapter itself is not stored. * the HTML is scanned for <link..., <style..., and <img tags. @@ -1279,6 +1338,7 @@ function addReferencePage($pageName, $fileName, $pageData, $reference, $external } $this->addFile($fileName, "ref_" . $reference, $pageData, "application/xhtml+xml"); + $this->extractIdAttributes("ref_" . $reference, $pageData); if ($reference !== Reference::TABLE_OF_CONTENTS || !isset($this->ncx->referencesList[$reference])) { $this->opf->addItemRef("ref_" . $reference); //, false); @@ -2054,6 +2114,11 @@ private function finalizeTOC() { $this->tocTitle = "Table of Contents"; } + $tocCssCls = ""; + if (!empty($this->tocCSSClass)) { + $tocCssCls = $this->tocCSSClass . " "; + } + $tocData = "\n"; if ($this->isEPubVersion2()) { @@ -2066,7 +2131,16 @@ private function finalizeTOC() { . "\n\n"; } $tocData .= $this->getViewportMetaLine(); - + $tocData .= "\n"; if (!empty($this->tocCssFileName)) { $tocData .= "tocCssFileName . "\" />\n"; } @@ -2087,18 +2161,20 @@ private function finalizeTOC() { /** @var $navPoint NavPoint */ $fileName = $navPoint->getContentSrc(); $level = $navPoint->getLevel() - 2; - $tocData .= "\t

" . str_repeat("      ", $level) . "" . $chapterName . "

\n"; + $tocData .= "\t

" + /* . str_repeat("      ", $level) . */ + . "" . $chapterName . "

\n"; } } else { if ($this->tocAddReferences === true) { if (array_key_exists($item, $this->ncx->referencesList)) { - $tocData .= "\t

ncx->referencesList[$item] . "\">" . $descriptive . "

\n"; + $tocData .= "\t

ncx->referencesList[$item] . "\">" . $descriptive . "

\n"; } else { if ($item === "toc") { - $tocData .= "\t

" . $this->tocTitle . "

\n"; + $tocData .= "\t

" . $this->tocTitle . "

\n"; } else { if ($item === "cover" && $this->isCoverImageSet) { - $tocData .= "\t

" . $descriptive . "

\n"; + $tocData .= "\t

" . $descriptive . "

\n"; } } } diff --git a/src/PHPePub/Core/Structure/OPF/Item.php b/src/PHPePub/Core/Structure/OPF/Item.php index fafe12b..2d68448 100644 --- a/src/PHPePub/Core/Structure/OPF/Item.php +++ b/src/PHPePub/Core/Structure/OPF/Item.php @@ -20,6 +20,8 @@ class Item { private $fallback = null; private $fallbackStyle = null; + private $indexPoints = array(); + /** * Class constructor. * @@ -151,4 +153,40 @@ function finalize($bookVersion = EPub::BOOK_VERSION_EPUB2) { return $item . "/>\n"; } + + /** + * @return array + */ + public function getIndexPoints() { + return $this->indexPoints; + } + + /** + * @param string $indexPoint + */ + public function addIndexPoint($indexPoint) { + $this->indexPoints[] = $indexPoint; + } + + /** + * @param string $indexPoint + * @return bool + */ + public function hasIndexPoint($indexPoint) { + return in_array($indexPoint, $this->indexPoints); + } + + /** + * @return null + */ + public function getId() { + return $this->id; + } + + /** + * @return null + */ + public function getHref() { + return $this->href; + } } diff --git a/src/PHPePub/Core/Structure/OPF/Manifest.php b/src/PHPePub/Core/Structure/OPF/Manifest.php index 2a385a7..dfaf032 100644 --- a/src/PHPePub/Core/Structure/OPF/Manifest.php +++ b/src/PHPePub/Core/Structure/OPF/Manifest.php @@ -55,4 +55,11 @@ function finalize($bookVersion = EPub::BOOK_VERSION_EPUB2) { return $manifest . "\t\n"; } + + /** + * @return array + */ + public function getItems() { + return $this->items; + } } diff --git a/src/PHPePub/Core/Structure/Opf.php b/src/PHPePub/Core/Structure/Opf.php index 42e6418..f25ce2c 100644 --- a/src/PHPePub/Core/Structure/Opf.php +++ b/src/PHPePub/Core/Structure/Opf.php @@ -1,6 +1,7 @@ manifest->addItem(new Item($id, $href, $mediaType, $properties)); } + /** + * @param string $id + * + * @return bool|Item Item if the id is found, else FALSE + */ + function getItemById($id) { + /** @var Item $item */ + foreach ($this->manifest->getItems() as $item) { + if ($item->getId() == $id) { + return $item; + } + } + return false; + } + + /** + * @param string $href + * + * @param bool $startsWith + * @return bool|array|Item Item if the href is found, else FALSE. If $startsWith is true, the returned object will be an array if any are found. + */ + function getItemByHref($href, $startsWith = false) { + $rv = array(); + + /** @var Item $item */ + foreach ($this->manifest->getItems() as $item) { + if (!$startsWith && $item->getHref() == $href) { + return $item; + } elseif($startsWith && BinStringStatic::startsWith($item->getHref(), $href)) { + $rv[] = $item; + } + } + if (sizeof($rv) > 0) { + return $rv; + } + return false; + } + /** * * Enter description here ...