* @copyright 2009-2014 A. Grandt * @license GNU LGPL 2.1 * @version 3.20 * @link http://www.phpclasses.org/package/6115 * @link https://github.com/Grandt/PHPePub * @uses Zip.php version 1.50; http://www.phpclasses.org/browse/package/6110.html or https://github.com/Grandt/PHPZip */ class EPub { const VERSION = 3.20; const REQ_ZIP_VERSION = 1.60; const IDENTIFIER_UUID = 'UUID'; const IDENTIFIER_URI = 'URI'; const IDENTIFIER_ISBN = 'ISBN'; /** Ignore all external references, and do not process the file for these */ const EXTERNAL_REF_IGNORE = 0; /** Process the file for external references and add them to the book */ const EXTERNAL_REF_ADD = 1; /** Process the file for external references and add them to the book, but remove images, and img tags */ const EXTERNAL_REF_REMOVE_IMAGES = 2; /** Process the file for external references and add them to the book, but replace images, and img tags with [image] */ const EXTERNAL_REF_REPLACE_IMAGES = 3; const DIRECTION_LEFT_TO_RIGHT = "ltr"; const DIRECTION_RIGHT_TO_LEFT = "rtl"; const BOOK_VERSION_EPUB2 = "2.0"; const BOOK_VERSION_EPUB3 = "3.0"; private $bookVersion = EPub::BOOK_VERSION_EPUB2; public $maxImageWidth = 768; public $maxImageHeight = 1024; public $splitDefaultSize = 250000; /** Gifs can crash some early ADE based readers, and are disabled by default. * getImage will convert these if it can, unless this is set to TRUE. */ public $isGifImagesEnabled = FALSE; public $isReferencesAddedToToc = TRUE; private $zip; private $title = ""; private $language = "en"; private $identifier = ""; private $identifierType = ""; private $description = ""; private $author = ""; private $authorSortKey = ""; private $publisherName = ""; private $publisherURL = ""; private $date = 0; private $rights = ""; private $coverage = ""; private $relation = ""; private $sourceURL = ""; private $chapterCount = 0; private $opf = NULL; private $ncx = NULL; private $isFinalized = FALSE; private $isCoverImageSet = FALSE; private $buildTOC = FALSE; private $tocTitle = NULL; private $tocFileName = NULL; private $tocCSSClass = NULL; private $tocAddReferences = FALSE; private $tocCssFileName = NULL; private $fileList = array(); private $writingDirection = EPub::DIRECTION_LEFT_TO_RIGHT; private $languageCode = "en"; /** * Used for building the TOC. * If this list is overwritten it MUST contain at least "text" as an element. */ public $referencesOrder = NULL; private $dateformat = 'Y-m-d\TH:i:s.000000P'; // ISO 8601 long private $dateformatShort = 'Y-m-d'; // short date format to placate ePubChecker. private $headerDateFormat = "D, d M Y H:i:s T"; protected $isCurlInstalled; protected $isGdInstalled; protected $isExifInstalled; protected $isFileGetContentsInstalled; protected $isFileGetContentsExtInstalled; private $bookRoot = "OEBPS/"; private $docRoot = NULL; private $EPubMark = TRUE; private $generator = ""; private $log = NULL; public $isLogging = TRUE; public $encodeHTML = FALSE; private $mimetypes = array( "js" => "application/x-javascript", "swf" => "application/x-shockwave-flash", "xht" => "application/xhtml+xml", "xhtml" => "application/xhtml+xml", "zip" => "application/zip", "aif" => "audio/x-aiff", "aifc" => "audio/x-aiff", "aiff" => "audio/x-aiff", "au" => "audio/basic", "kar" => "audio/midi", "m3u" => "audio/x-mpegurl", "mid" => "audio/midi", "midi" => "audio/midi", "mp2" => "audio/mpeg", "mp3" => "audio/mpeg", "mpga" => "audio/mpeg", "oga" => "audio/ogg", "ogg" => "audio/ogg", "ra" => "audio/x-realaudio", "ram" => "audio/x-pn-realaudio", "rm" => "audio/x-pn-realaudio", "rpm" => "audio/x-pn-realaudio-plugin", "snd" => "audio/basic", "wav" => "audio/x-wav", "bmp" => "image/bmp", "djv" => "image/vnd.djvu", "djvu" => "image/vnd.djvu", "gif" => "image/gif", "ief" => "image/ief", "jpe" => "image/jpeg", "jpeg" => "image/jpeg", "jpg" => "image/jpeg", "pbm" => "image/x-portable-bitmap", "pgm" => "image/x-portable-graymap", "png" => "image/png", "pnm" => "image/x-portable-anymap", "ppm" => "image/x-portable-pixmap", "ras" => "image/x-cmu-raster", "rgb" => "image/x-rgb", "tif" => "image/tif", "tiff" => "image/tiff", "wbmp" => "image/vnd.wap.wbmp", "xbm" => "image/x-xbitmap", "xpm" => "image/x-xpixmap", "xwd" => "image/x-windowdump", "asc" => "text/plain", "css" => "text/css", "etx" => "text/x-setext", "htm" => "text/html", "html" => "text/html", "rtf" => "text/rtf", "rtx" => "text/richtext", "sgm" => "text/sgml", "sgml" => "text/sgml", "tsv" => "text/tab-seperated-values", "txt" => "text/plain", "wml" => "text/vnd.wap.wml", "wmls" => "text/vnd.wap.wmlscript", "xml" => "text/xml", "xsl" => "text/xml", "avi" => "video/x-msvideo", "mov" => "video/quicktime", "movie" => "video/x-sgi-movie", "mp4" => "video/mp4", "mpe" => "video/mpeg", "mpeg" => "video/mpeg", "mpg" => "video/mpeg", "mxu" => "video/vnd.mpegurl", "ogv" => "video/ogg", "qt" => "video/quicktime", "webm" => "video/webm"); // These are the ONLY allowed types in that these are the ones ANY reader must support, any other MUST have the fallback attribute pointing to one of these. private $coreMediaTypes = array("image/gif", "image/jpeg", "image/png", "image/svg+xml", "application/xhtml+xml", "application/x-dtbook+xml", "application/xml", "application/x-dtbncx+xml", "text/css", "text/x-oeb1-css", "text/x-oeb1-document"); private $opsContentTypes = array("application/xhtml+xml", "application/x-dtbook+xml", "application/xml", "application/x-dtbncx+xml", "text/x-oeb1-document"); private $forbiddenCharacters = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%"); private $htmlContentHeader = "\n\n\n
\nEPub version " . self::VERSION . " requires Zip.php at version " . self::REQ_ZIP_VERSION . " or higher.
You can obtain the latest version from http://www.phpclasses.org/browse/package/6110.html.
processCSSExternalReferences
for explanation. Default is EPub::EXTERNAL_REF_IGNORE.
* @param string $baseDir Default is "", meaning it is pointing to the document root. NOT used if $externalReferences is set to EPub::EXTERNAL_REF_IGNORE.
*
* @return bool $success
*/
function addCSSFile($fileName, $fileId, $fileData, $externalReferences = EPub::EXTERNAL_REF_IGNORE, $baseDir = "") {
if ($this->isFinalized || array_key_exists($fileName, $this->fileList)) {
return FALSE;
}
$fileName = Zip::getRelativePath($fileName);
$fileName = preg_replace('#^[/\.]+#i', "", $fileName);
if ($externalReferences !== EPub::EXTERNAL_REF_IGNORE) {
$cssDir = pathinfo($fileName);
$cssDir = preg_replace('#^[/\.]+#i', "", $cssDir["dirname"] . "/");
if (!empty($cssDir)) {
$cssDir = preg_replace('#[^/]+/#i', "../", $cssDir);
}
$this->processCSSExternalReferences($fileData, $externalReferences, $baseDir, $cssDir);
}
$this->addFile($fileName, "css_" . $fileId, $fileData, "text/css");
return TRUE;
}
/**
* Add a chapter to the book, as a chapter should not exceed 250kB, you can parse an array with multiple parts as $chapterData.
* These will still only show up as a single chapter in the book TOC.
*
* @param string $chapterName Name of the chapter, will be use din the TOC
* @param string $fileName Filename to use for the chapter, must be unique for the book.
* @param string $chapter Chapter text in XHTML or array $chapterData valid XHTML data for the chapter. File should NOT exceed 250kB.
* @param bool $autoSplit Should the chapter be split if it exceeds the default split size? Default=FALSE, only used if $chapterData is a string.
* @param int $externalReferences How to handle external references, EPub::EXTERNAL_REF_IGNORE, EPub::EXTERNAL_REF_ADD or EPub::EXTERNAL_REF_REMOVE_IMAGES? See documentation for processChapterExternalReferences
for explanation. Default is EPub::EXTERNAL_REF_IGNORE.
* @param string $baseDir Default is "", meaning it is pointing to the document root. NOT used if $externalReferences is set to EPub::EXTERNAL_REF_IGNORE.
* @return mixed $success FALSE if the addition failed, else the new NavPoint.
*/
function addChapter($chapterName, $fileName, $chapterData = NULL, $autoSplit = FALSE, $externalReferences = EPub::EXTERNAL_REF_IGNORE, $baseDir = "") {
if ($this->isFinalized) {
return FALSE;
}
$fileName = Zip::getRelativePath($fileName);
$fileName = preg_replace('#^[/\.]+#i', "", $fileName);
$fileName = $this->sanitizeFileName($fileName);
$chapter = $chapterData;
if ($autoSplit && is_string($chapterData) && mb_strlen($chapterData) > $this->splitDefaultSize) {
$splitter = new EPubChapterSplitter();
$chapterArray = $splitter->splitChapter($chapterData);
if (count($chapterArray) > 1) {
$chapter = $chapterArray;
}
}
if (!empty($chapter) && is_string($chapter)) {
if ($externalReferences !== EPub::EXTERNAL_REF_IGNORE) {
$htmlDirInfo = pathinfo($fileName);
$htmlDir = preg_replace('#^[/\.]+#i', "", $htmlDirInfo["dirname"] . "/");
$this->processChapterExternalReferences($chapter, $externalReferences, $baseDir, $htmlDir);
}
if ($this->encodeHTML === TRUE) {
$chapter = $this->encodeHtml($chapter);
}
$this->chapterCount++;
$this->addFile($fileName, "chapter" . $this->chapterCount, $chapter, "application/xhtml+xml");
$this->opf->addItemRef("chapter" . $this->chapterCount);
$navPoint = new NavPoint($this->decodeHtmlEntities($chapterName), $fileName, "chapter" . $this->chapterCount);
$this->ncx->addNavPoint($navPoint);
$this->ncx->chapterList[$chapterName] = $navPoint;
} else if (is_array($chapter)) {
$fileNameParts = pathinfo($fileName);
$extension = $fileNameParts['extension'];
$name = $fileNameParts['filename'];
$partCount = 0;
$this->chapterCount++;
$oneChapter = each($chapter);
while ($oneChapter) {
list($k, $v) = $oneChapter;
if ($this->encodeHTML === TRUE) {
$v = $this->encodeHtml($v);
}
if ($externalReferences !== EPub::EXTERNAL_REF_IGNORE) {
$this->processChapterExternalReferences($v, $externalReferences, $baseDir);
}
$partCount++;
$partName = $name . "_" . $partCount;
$this->addFile($partName . "." . $extension, $partName, $v, "application/xhtml+xml");
$this->opf->addItemRef($partName);
$oneChapter = each($chapter);
}
$partName = $name . "_1." . $extension;
$navPoint = new NavPoint($this->decodeHtmlEntities($chapterName), $partName, $partName);
$this->ncx->addNavPoint($navPoint);
$this->ncx->chapterList[$chapterName] = $navPoint;
} else if (!isset($chapterData) && strpos($fileName, "#") > 0) {
$this->chapterCount++;
//$this->opf->addItemRef("chapter" . $this->chapterCount);
$navPoint = new NavPoint($this->decodeHtmlEntities($chapterName), $fileName, "chapter" . $this->chapterCount);
$this->ncx->addNavPoint($navPoint);
$this->ncx->chapterList[$chapterName] = $navPoint;
} else if (!isset($chapterData) && $fileName=="TOC.xhtml") {
$this->chapterCount++;
$this->opf->addItemRef("toc");
$navPoint = new NavPoint($this->decodeHtmlEntities($chapterName), $fileName, "chapter" . $this->chapterCount);
$this->ncx->addNavPoint($navPoint);
$this->ncx->chapterList[$chapterName] = $navPoint;
}
return $navPoint;
}
/**
* Add one chapter level.
*
* Subsequent chapters will be added to this level.
*
* @param string $navTitle
* @param string $navId
* @param string $navClass
* @param int $isNavHidden
* @param string $writingDirection
* @return NavPoint The new NavPoint for that level.
*/
function subLevel($navTitle = NULL, $navId = NULL, $navClass = NULL, $isNavHidden = FALSE, $writingDirection = NULL) {
return $this->ncx->subLevel($this->decodeHtmlEntities($navTitle), $navId, $navClass, $isNavHidden, $writingDirection);
}
/**
* Step back one chapter level.
*
* Subsequent chapters will be added to this chapters parent level.
*/
function backLevel() {
$this->ncx->backLevel();
}
/**
* Step back to the root level.
*
* Subsequent chapters will be added to the rooot NavMap.
*/
function rootLevel() {
$this->ncx->rootLevel();
}
/**
* 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) {
$this->ncx->setCurrentLevel($newLevel);
}
/**
* Get current level count.
* The indentation of the current structure point.
*
* @return current level count;
*/
function getCurrentLevel() {
return $this->ncx->getCurrentLevel();
}
/**
* Wrap ChapterContent with Head and Footer
*
* @param $content
* @return string $content
*/
private function wrapChapter($content) {
return $this->htmlContentHeader . "\n" . $content . "\n" . $this->htmlContentFooter;
}
/**
* Reference pages is usually one or two pages for items such as Table of Contents, reference lists, Author notes or Acknowledgements.
* These do not show up in the regular navigation list.
*
* As they are supposed to be short.
*
* @param string $pageName Name of the chapter, will be use din the TOC
* @param string $fileName Filename to use for the chapter, must be unique for the book.
* @param string $pageData Page content in XHTML. File should NOT exceed 250kB.
* @param string $reference Reference key
* @param int $externalReferences How to handle external references. See documentation for processChapterExternalReferences
for explanation. Default is EPub::EXTERNAL_REF_IGNORE.
* @param string $baseDir Default is "", meaning it is pointing to the document root. NOT used if $externalReferences is set to EPub::EXTERNAL_REF_IGNORE.
* @return bool $success
*/
function addReferencePage($pageName, $fileName, $pageData, $reference, $externalReferences = EPub::EXTERNAL_REF_IGNORE, $baseDir = "") {
if ($this->isFinalized) {
return FALSE;
}
$fileName = Zip::getRelativePath($fileName);
$fileName = preg_replace('#^[/\.]+#i', "", $fileName);
if (!empty($pageData) && is_string($pageData)) {
if ($this->encodeHTML === TRUE) {
$pageData = $this->encodeHtml($pageData);
}
$this->wrapChapter($pageData);
if ($externalReferences !== EPub::EXTERNAL_REF_IGNORE) {
$htmlDirInfo = pathinfo($fileName);
$htmlDir = preg_replace('#^[/\.]+#i', "", $htmlDirInfo["dirname"] . "/");
$this->processChapterExternalReferences($pageData, $externalReferences, $baseDir, $htmlDir);
}
$this->addFile($fileName, "ref_" . $reference, $pageData, "application/xhtml+xml");
if ($reference !== Reference::TABLE_OF_CONTENTS || !isset($this->ncx->referencesList[$reference])) {
$this->opf->addItemRef("ref_" . $reference, FALSE);
$this->opf->addReference($reference, $pageName, $fileName);
$this->ncx->referencesList[$reference] = $fileName;
$this->ncx->referencesName[$reference] = $pageName;
}
return TRUE;
}
return TRUE;
}
/**
* Add custom metadata to the book.
*
* It is up to the builder to make sure there are no collisions. Metadata are just key value pairs.
*
* @param string $name
* @param string $content
*/
function addCustomMetadata($name, $content) {
$this->opf->addMeta($name, $content);
}
/**
* Add DublinCore metadata to the book
*
* Use the DublinCore constants included in EPub, ie DublinCore::DATE
*
* @param string $dublinCore name
* @param string $value
*/
function addDublinCoreMetadata($dublinCoreConstant, $value) {
if ($this->isFinalized) {
return;
}
$this->opf->addDCMeta($dublinCoreConstant, $this->decodeHtmlEntities($value));
}
/**
* Add a cover image to the book.
* If the $imageData is not set, the function assumes the $fileName is the path to the image file.
*
* The styling and structure of the generated XHTML is heavily inspired by the XHTML generated by Calibre.
*
* @param string $fileName Filename to use for the image, must be unique for the book.
* @param string $imageData Binary image data
* @param string $mimetype Image mimetype, such as "image/jpeg" or "image/png".
* @return bool $success
*/
function setCoverImage($fileName, $imageData = NULL, $mimetype = NULL,$bookTitle) {
if ($this->isFinalized || $this->isCoverImageSet || array_key_exists("CoverPage.html", $this->fileList)) {
return FALSE;
}
if ($imageData == NULL) {
// assume $fileName is the valid file path.
if (!file_exists($fileName)) {
// Attempt to locate the file using the doc root.
$rp = realpath($this->docRoot . "/" . $fileName);
if ($rp !== FALSE) {
// only assign the docroot path if it actually exists there.
$fileName = $rp;
}
}
$image = $this->getImage($fileName);
$imageData = $image['image'];
$mimetype = $image['mime'];
$fileName = preg_replace("#\.[^\.]+$#", "." . $image['ext'], $fileName);
}
$path = pathinfo($fileName);
$imgPath = "images/" . $path["basename"];
if (empty($mimetype) && file_exists($fileName)) {
list($width, $height, $type, $attr) = getimagesize($fileName);
$mimetype = image_type_to_mime_type($type);
}
if (empty($mimetype)) {
$ext = strtolower($path['extension']);
if ($ext == "jpg") {
$ext = "jpeg";
}
$mimetype = "image/" . $ext;
}
$coverPage = "";
if ($this->isEPubVersion2()) {
$coverPage = "\n"
. "\n"
. "\n"
. "\t\n"
. "\t\t\n"
. "\t\t<img src="../images/image.png"/>
*
* $externalReferences determines how the function will handle external references.
*
* @param mixed &$doc (referenced)
* @param int $externalReferences How to handle external references, EPub::EXTERNAL_REF_IGNORE, EPub::EXTERNAL_REF_ADD or EPub::EXTERNAL_REF_REMOVE_IMAGES? Default is EPub::EXTERNAL_REF_ADD.
* @param string $baseDir Default is "", meaning it is pointing to the document root.
* @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
*
* @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
*/
protected function processChapterExternalReferences(&$doc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "") {
if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
return FALSE;
}
$backPath = preg_replace('#[^/]+/#i', "../", $htmlDir);
$isDocAString = is_string($doc);
$xmlDoc = NULL;
if ($isDocAString) {
$xmlDoc = new DOMDocument();
@$xmlDoc->loadHTML($doc);
} else {
$xmlDoc = $doc;
}
$this->processChapterStyles($xmlDoc, $externalReferences, $baseDir, $htmlDir);
$this->processChapterLinks($xmlDoc, $externalReferences, $baseDir, $htmlDir, $backPath);
$this->processChapterImages($xmlDoc, $externalReferences, $baseDir, $htmlDir, $backPath);
$this->processChapterSources($xmlDoc, $externalReferences, $baseDir, $htmlDir, $backPath);
if ($isDocAString) {
//$html = $xmlDoc->saveXML();
$htmlNode = $xmlDoc->getElementsByTagName("html");
$headNode = $xmlDoc->getElementsByTagName("head");
$bodyNode = $xmlDoc->getElementsByTagName("body");
$htmlNS = "";
for ($index = 0; $index < $htmlNode->item(0)->attributes->length; $index++) {
$nodeName = $htmlNode->item(0)->attributes->item($index)->nodeName;
$nodeValue = $htmlNode->item(0)->attributes->item($index)->nodeValue;
if ($nodeName != "xmlns") {
$htmlNS .= " $nodeName=\"$nodeValue\"";
}
}
$xml = new DOMDocument('1.0', "utf-8");
$xml->lookupPrefix("http://www.w3.org/1999/xhtml");
$xml->preserveWhiteSpace = FALSE;
$xml->formatOutput = TRUE;
$xml2Doc = new DOMDocument('1.0', "utf-8");
$xml2Doc->lookupPrefix("http://www.w3.org/1999/xhtml");
$xml2Doc->loadXML("\n\n\n\n");
$html = $xml2Doc->getElementsByTagName("html")->item(0);
$html->appendChild($xml2Doc->importNode($headNode->item(0), TRUE));
$html->appendChild($xml2Doc->importNode($bodyNode->item(0), TRUE));
// force pretty printing and correct formatting, should not be needed, but it is.
$xml->loadXML($xml2Doc->saveXML());
$doc = $xml->saveXML();
if (!$this->isEPubVersion2()) {
$doc = preg_replace('#^\s*\s*#im', '', $doc);
}
}
return TRUE;
}
/**
* Process images referenced from an CSS file to the book.
*
* $externalReferences determins how the function will handle external references.
*
* @param string &$cssFile (referenced)
* @param int $externalReferences How to handle external references, EPub::EXTERNAL_REF_IGNORE, EPub::EXTERNAL_REF_ADD or EPub::EXTERNAL_REF_REMOVE_IMAGES? Default is EPub::EXTERNAL_REF_ADD.
* @param string $baseDir Default is "", meaning it is pointing to the document root.
* @param string $cssDir The of the CSS file's directory from the root of the archive.
*
* @return bool FALSE if unsuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
*/
protected function processCSSExternalReferences(&$cssFile, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $cssDir = "") {
if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
return FALSE;
}
$backPath = preg_replace('#[^/]+/#i', "../", $cssDir);
$imgs = null;
preg_match_all('#url\s*\([\'\"\s]*(.+?)[\'\"\s]*\)#im', $cssFile, $imgs, PREG_SET_ORDER);
$itemCount = count($imgs);
for ($idx = 0; $idx < $itemCount; $idx++) {
$img = $imgs[$idx];
if ($externalReferences === EPub::EXTERNAL_REF_REMOVE_IMAGES || $externalReferences === EPub::EXTERNAL_REF_REPLACE_IMAGES) {
$cssFile = str_replace($img[0], "", $cssFile);
} else {
$source = $img[1];
$pathData = pathinfo($source);
$internalSrc = $pathData['basename'];
$internalPath = "";
$isSourceExternal = FALSE;
if ($this->resolveImage($source, $internalPath, $internalSrc, $isSourceExternal, $baseDir, $cssDir, $backPath)) {
$cssFile = str_replace($img[0], "url('" . $backPath . $internalPath . "')", $cssFile);
} else if ($isSourceExternal) {
$cssFile = str_replace($img[0], "", $cssFile); // External image is missing
} // else do nothing, if the image is local, and missing, assume it's been generated.
}
}
return TRUE;
}
/**
* Process style tags in a DOMDocument. Styles will be passed as CSS files and reinserted into the document.
*
* @param DOMDocument &$xmlDoc (referenced)
* @param int $externalReferences How to handle external references, EPub::EXTERNAL_REF_IGNORE, EPub::EXTERNAL_REF_ADD or EPub::EXTERNAL_REF_REMOVE_IMAGES? Default is EPub::EXTERNAL_REF_ADD.
* @param string $baseDir Default is "", meaning it is pointing to the document root.
* @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
*
* @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
*/
protected function processChapterStyles(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "") {
if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
return FALSE;
}
// process inlined CSS styles in style tags.
$styles = $xmlDoc->getElementsByTagName("style");
$styleCount = $styles->length;
for ($styleIdx = 0; $styleIdx < $styleCount; $styleIdx++) {
$style = $styles->item($styleIdx);
$styleData = preg_replace('#[/\*\s]*\<\!\[CDATA\[[\s\*/]*#im', "", $style->nodeValue);
$styleData = preg_replace('#[/\*\s]*\]\]\>[\s\*/]*#im', "", $styleData);
$this->processCSSExternalReferences($styleData, $externalReferences, $baseDir, $htmlDir);
$style->nodeValue = "\n" . trim($styleData) . "\n";
}
return TRUE;
}
/**
* Process link tags in a DOMDocument. Linked files will be loaded into the archive, and the link src will be rewritten to point to that location.
* Link types text/css will be passed as CSS files.
*
* @param DOMDocument &$xmlDoc (referenced)
* @param int $externalReferences How to handle external references, EPub::EXTERNAL_REF_IGNORE, EPub::EXTERNAL_REF_ADD or EPub::EXTERNAL_REF_REMOVE_IMAGES? Default is EPub::EXTERNAL_REF_ADD.
* @param string $baseDir Default is "", meaning it is pointing to the document root.
* @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
* @param string $backPath The path to get back to the root of the archive from $htmlDir.
*
* @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
*/
protected function processChapterLinks(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "", $backPath = "") {
if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
return FALSE;
}
// process link tags.
$links = $xmlDoc->getElementsByTagName("link");
$linkCount = $links->length;
for ($linkIdx = 0; $linkIdx < $linkCount; $linkIdx++) {
$link = $links->item($linkIdx);
$source = $link->attributes->getNamedItem("href")->nodeValue;
$sourceData = NULL;
$pathData = pathinfo($source);
$internalSrc = $pathData['basename'];
if (preg_match('#^(http|ftp)s?://#i', $source) == 1) {
$urlinfo = parse_url($source);
if (strpos($urlinfo['path'], $baseDir."/") !== FALSE) {
$internalSrc = substr($urlinfo['path'], strpos($urlinfo['path'], $baseDir."/") + strlen($baseDir) + 1);
}
@$sourceData = getFileContents($source);
} else if (strpos($source, "/") === 0) {
@$sourceData = file_get_contents($this->docRoot . $source);
} else {
@$sourceData = file_get_contents($this->docRoot . $baseDir . "/" . $source);
}
if (!empty($sourceData)) {
if (!array_key_exists($internalSrc, $this->fileList)) {
$mime = $link->attributes->getNamedItem("type")->nodeValue;
if (empty($mime)) {
$mime = "text/plain";
}
if ($mime == "text/css") {
$this->processCSSExternalReferences($sourceData, $externalReferences, $baseDir, $htmlDir);
$this->addCSSFile($internalSrc, $internalSrc, $sourceData, EPub::EXTERNAL_REF_IGNORE, $baseDir);
$link->setAttribute("href", $backPath . $internalSrc);
} else {
$this->addFile($internalSrc, $internalSrc, $sourceData, $mime);
}
$this->fileList[$internalSrc] = $source;
} else {
$link->setAttribute("href", $backPath . $internalSrc);
}
} // else do nothing, if the link is local, and missing, assume it's been generated.
}
return TRUE;
}
/**
* Process img tags in a DOMDocument.
* $externalReferences will determine what will happen to these images, and the img src will be rewritten accordingly.
*
* @param DOMDocument &$xmlDoc (referenced)
* @param int $externalReferences How to handle external references, EPub::EXTERNAL_REF_IGNORE, EPub::EXTERNAL_REF_ADD or EPub::EXTERNAL_REF_REMOVE_IMAGES? Default is EPub::EXTERNAL_REF_ADD.
* @param string $baseDir Default is "", meaning it is pointing to the document root.
* @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
* @param string $backPath The path to get back to the root of the archive from $htmlDir.
*
* @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
*/
protected function processChapterImages(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "", $backPath = "") {
if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
return FALSE;
}
// process img tags.
$postProcDomElememts = array();
$images = $xmlDoc->getElementsByTagName("img");
$itemCount = $images->length;
for ($idx = 0; $idx < $itemCount; $idx++) {
$img = $images->item($idx);
if ($externalReferences === EPub::EXTERNAL_REF_REMOVE_IMAGES) {
$postProcDomElememts[] = $img;
} else if ($externalReferences === EPub::EXTERNAL_REF_REPLACE_IMAGES) {
$altNode = $img->attributes->getNamedItem("alt");
$alt = "image";
if ($altNode !== NULL && strlen($altNode->nodeValue) > 0) {
$alt = $altNode->nodeValue;
}
$postProcDomElememts[] = array($img, $this->createDomFragment($xmlDoc, "[" . $alt . "]"));
} else {
$source = $img->attributes->getNamedItem("src")->nodeValue;
$parsedSource = parse_url($source);
$internalSrc = $this->sanitizeFileName(urldecode(pathinfo($parsedSource['path'], PATHINFO_BASENAME)));
$internalPath = "";
$isSourceExternal = FALSE;
if ($this->resolveImage($source, $internalPath, $internalSrc, $isSourceExternal, $baseDir, $htmlDir, $backPath)) {
$img->setAttribute("src", $backPath . $internalPath);
} else if ($isSourceExternal) {
$postProcDomElememts[] = $img; // External image is missing
} // else do nothing, if the image is local, and missing, assume it's been generated.
}
}
foreach ($postProcDomElememts as $target) {
if (is_array($target)) {
$target[0]->parentNode->replaceChild($target[1], $target[0]);
} else {
$target->parentNode->removeChild($target);
}
}
return TRUE;
}
/**
* Process source tags in a DOMDocument.
* $externalReferences will determine what will happen to these images, and the img src will be rewritten accordingly.
*
* @param DOMDocument &$xmlDoc (referenced)
* @param int $externalReferences How to handle external references, EPub::EXTERNAL_REF_IGNORE, EPub::EXTERNAL_REF_ADD or EPub::EXTERNAL_REF_REMOVE_IMAGES? Default is EPub::EXTERNAL_REF_ADD.
* @param string $baseDir Default is "", meaning it is pointing to the document root.
* @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
* @param string $backPath The path to get back to the root of the archive from $htmlDir.
*
* @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
*/
protected function processChapterSources(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "", $backPath = "") {
if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
return FALSE;
}
if ($this->bookVersion !== EPub::BOOK_VERSION_EPUB3) {
// ePub 2 does not support multimedia formats, and they must be removed.
$externalReferences = EPub::EXTERNAL_REF_REMOVE_IMAGES;
}
$postProcDomElememts = array();
$images = $xmlDoc->getElementsByTagName("source");
$itemCount = $images->length;
for ($idx = 0; $idx < $itemCount; $idx++) {
$img = $images->item($idx);
if ($externalReferences === EPub::EXTERNAL_REF_REMOVE_IMAGES) {
$postProcDomElememts[] = $img;
} else if ($externalReferences === EPub::EXTERNAL_REF_REPLACE_IMAGES) {
$altNode = $img->attributes->getNamedItem("alt");
$alt = "image";
if ($altNode !== NULL && strlen($altNode->nodeValue) > 0) {
$alt = $altNode->nodeValue;
}
$postProcDomElememts[] = array($img, $this->createDomFragment($xmlDoc, "[" . $alt . "]"));
} else {
$source = $img->attributes->getNamedItem("src")->nodeValue;
$parsedSource = parse_url($source);
$internalSrc = $this->sanitizeFileName(urldecode(pathinfo($parsedSource['path'], PATHINFO_BASENAME)));
$internalPath = "";
$isSourceExternal = FALSE;
if ($this->resolveMedia($source, $internalPath, $internalSrc, $isSourceExternal, $baseDir, $htmlDir, $backPath)) {
$img->setAttribute("src", $backPath . $internalPath);
} else if ($isSourceExternal) {
$postProcDomElememts[] = $img; // External image is missing
} // else do nothing, if the image is local, and missing, assume it's been generated.
}
}
}
/**
* Resolve an image src and determine it's target location and add it to the book.
*
* @param string $source Image Source link.
* @param string &$internalPath (referenced) Return value, will be set to the target path and name in the book.
* @param string &$internalSrc (referenced) Return value, will be set to the target name in the book.
* @param string &$isSourceExternal (referenced) Return value, will be set to TRUE if the image originated from a full URL.
* @param string $baseDir Default is "", meaning it is pointing to the document root.
* @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
* @param string $backPath The path to get back to the root of the archive from $htmlDir.
*/
protected function resolveImage($source, &$internalPath, &$internalSrc, &$isSourceExternal, $baseDir = "", $htmlDir = "", $backPath = "") {
if ($this->isFinalized) {
return FALSE;
}
$imageData = NULL;
if (preg_match('#^(http|ftp)s?://#i', $source) == 1) {
$urlinfo = parse_url($source);
$urlPath = pathinfo($urlinfo['path']);
if (strpos($urlinfo['path'], $baseDir."/") !== FALSE) {
$internalSrc = $this->sanitizeFileName(urldecode(substr($urlinfo['path'], strpos($urlinfo['path'], $baseDir."/") + strlen($baseDir) + 1)));
}
$internalPath = $urlinfo["scheme"] . "/" . $urlinfo["host"] . "/" . pathinfo($urlinfo["path"], PATHINFO_DIRNAME);
$isSourceExternal = TRUE;
$imageData = $this->getImage($source);
} else if (strpos($source, "/") === 0) {
$internalPath = pathinfo($source, PATHINFO_DIRNAME);
$path = $source;
if (!file_exists($path)) {
$path = $this->docRoot . $path;
}
$imageData = $this->getImage($path);
} else {
$internalPath = $htmlDir . "/" . preg_replace('#^[/\.]+#', '', pathinfo($source, PATHINFO_DIRNAME));
$path = $baseDir . "/" . $source;
if (!file_exists($path)) {
$path = $this->docRoot . $path;
}
$imageData = $this->getImage($path);
}
if ($imageData !== FALSE) {
$iSrcInfo = pathinfo($internalSrc);
if (!empty($imageData['ext']) && $imageData['ext'] != $iSrcInfo['extension']) {
$internalSrc = $iSrcInfo['filename'] . "." . $imageData['ext'];
}
$internalPath = Zip::getRelativePath("images/" . $internalPath . "/" . $internalSrc);
if (!array_key_exists($internalPath, $this->fileList)) {
$this->addFile($internalPath, "i_" . $internalSrc, $imageData['image'], $imageData['mime']);
$this->fileList[$internalPath] = $source;
}
return TRUE;
}
return FALSE;
}
/**
* Resolve a media src and determine it's target location and add it to the book.
*
* @param string $source Source link.
* @param string $internalPath (referenced) Return value, will be set to the target path and name in the book.
* @param string $internalSrc (referenced) Return value, will be set to the target name in the book.
* @param string $isSourceExternal (referenced) Return value, will be set to TRUE if the image originated from a full URL.
* @param string $baseDir Default is "", meaning it is pointing to the document root.
* @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
* @param string $backPath The path to get back to the root of the archive from $htmlDir.
*/
protected function resolveMedia($source, &$internalPath, &$internalSrc, &$isSourceExternal, $baseDir = "", $htmlDir = "", $backPath = "") {
if ($this->isFinalized) {
return FALSE;
}
$mediaPath = NULL;
$tmpFile;
if (preg_match('#^(http|ftp)s?://#i', $source) == 1) {
$urlinfo = parse_url($source);
if (strpos($urlinfo['path'], $baseDir."/") !== FALSE) {
$internalSrc = substr($urlinfo['path'], strpos($urlinfo['path'], $baseDir."/") + strlen($baseDir) + 1);
}
$internalPath = $urlinfo["scheme"] . "/" . $urlinfo["host"] . "/" . pathinfo($urlinfo["path"], PATHINFO_DIRNAME);
$isSourceExternal = TRUE;
$mediaPath = $this->getFileContents($source, true);
$tmpFile = $mediaPath;
} else if (strpos($source, "/") === 0) {
$internalPath = pathinfo($source, PATHINFO_DIRNAME);
$mediaPath = $source;
if (!file_exists($mediaPath)) {
$mediaPath = $this->docRoot . $mediaPath;
}
} else {
$internalPath = $htmlDir . "/" . preg_replace('#^[/\.]+#', '', pathinfo($source, PATHINFO_DIRNAME));
$mediaPath = $baseDir . "/" . $source;
if (!file_exists($mediaPath)) {
$mediaPath = $this->docRoot . $mediaPath;
}
}
if ($mediaPath !== FALSE) {
$mime = $this->getMime($source);
$internalPath = Zip::getRelativePath("media/" . $internalPath . "/" . $internalSrc);
if (!array_key_exists($internalPath, $this->fileList) &&
$this->addLargeFile($internalPath, "m_" . $internalSrc, $mediaPath, $mime)) {
$this->fileList[$internalPath] = $source;
}
if (isset($tmpFile)) {
unlink($tmpFile);
}
return TRUE;
}
return FALSE;
}
/**
* Get Book Chapter count.
*
* @access public
* @return number of chapters
*/
function getChapterCount() {
return $this->chapterCount;
}
/**
* Book title, mandatory.
*
* Used for the dc:title metadata parameter in the OPF file as well as the DocTitle attribute in the NCX file.
*
* @param string $title
* @access public
* @return bool $success
*/
function setTitle($title) {
if ($this->isFinalized) {
return FALSE;
}
$this->title = $title;
return TRUE;
}
/**
* Get Book title.
*
* @access public
* @return $title
*/
function getTitle() {
return $this->title;
}
/**
* Book language, mandatory
*
* Use the RFC3066 Language codes, such as "en", "da", "fr" etc.
* Defaults to "en".
*
* Used for the dc:language metadata parameter in the OPF file.
*
* @param string $language
* @access public
* @return bool $success
*/
function setLanguage($language) {
if ($this->isFinalized || mb_strlen($language) != 2) {
return FALSE;
}
$this->language = $language;
return TRUE;
}
/**
* Get Book language.
*
* @access public
* @return $language
*/
function getLanguage() {
return $this->language;
}
/**
* Unique book identifier, mandatory.
* Use the URI, or ISBN if available.
*
* An unambiguous reference to the resource within a given context.
*
* Recommended best practice is to identify the resource by means of a
* string conforming to a formal identification system.
*
* Used for the dc:identifier metadata parameter in the OPF file, as well
* as dtb:uid in the NCX file.
*
* Identifier type should only be:
* EPub::IDENTIFIER_URI
* EPub::IDENTIFIER_ISBN
* EPub::IDENTIFIER_UUID
*
* @param string $identifier
* @param string $identifierType
* @access public
* @return bool $success
*/
function setIdentifier($identifier, $identifierType) {
if ($this->isFinalized || ($identifierType !== EPub::IDENTIFIER_URI && $identifierType !== EPub::IDENTIFIER_ISBN && $identifierType !== EPub::IDENTIFIER_UUID)) {
return FALSE;
}
$this->identifier = $identifier;
$this->identifierType = $identifierType;
return TRUE;
}
/**
* Get Book identifier.
*
* @access public
* @return $identifier
*/
function getIdentifier() {
return $this->identifier;
}
/**
* Get Book identifierType.
*
* @access public
* @return $identifierType
*/
function getIdentifierType() {
return $this->identifierType;
}
/**
* Book description, optional.
*
* An account of the resource.
*
* Description may include but is not limited to: an abstract, a table of
* contents, a graphical representation, or a free-text account of the
* resource.
*
* Used for the dc:source metadata parameter in the OPF file
*
* @param string $description
* @access public
* @return bool $success
*/
function setDescription($description) {
if ($this->isFinalized) {
return FALSE;
}
$this->description = $description;
return TRUE;
}
/**
* Get Book description.
*
* @access public
* @return $description
*/
function getDescription() {
return $this->description;
}
/**
* Book author or creator, optional.
* The $authorSortKey is basically how the name is to be sorted, usually
* it's "Lastname, First names" where the $author is the straight
* "Firstnames Lastname"
*
* An entity primarily responsible for making the resource.
*
* Examples of a Creator include a person, an organization, or a service.
* Typically, the name of a Creator should be used to indicate the entity.
*
* Used for the dc:creator metadata parameter in the OPF file and the
* docAuthor attribure in the NCX file.
* The sort key is used for the opf:file-as attribute in dc:creator.
*
* @param string $author
* @param string $authorSortKey
* @access public
* @return bool $success
*/
function setAuthor($author, $authorSortKey) {
if ($this->isFinalized) {
return FALSE;
}
$this->author = $author;
$this->authorSortKey = $authorSortKey;
return TRUE;
}
/**
* Get Book author.
*
* @access public
* @return $author
*/
function getAuthor() {
return $this->author;
}
/**
* Publisher Information, optional.
*
* An entity responsible for making the resource available.
*
* Examples of a Publisher include a person, an organization, or a service.
* Typically, the name of a Publisher should be used to indicate the entity.
*
* Used for the dc:publisher and dc:relation metadata parameters in the OPF file.
*
* @param string $publisherName
* @param string $publisherURL
* @access public
* @return bool $success
*/
function setPublisher($publisherName, $publisherURL) {
if ($this->isFinalized) {
return FALSE;
}
$this->publisherName = $publisherName;
$this->publisherURL = $publisherURL;
return TRUE;
}
/**
* Get Book publisherName.
*
* @access public
* @return $publisherName
*/
function getPublisherName() {
return $this->publisherName;
}
/**
* Get Book publisherURL.
*
* @access public
* @return $publisherURL
*/
function getPublisherURL() {
return $this->publisherURL;
}
/**
* Release date, optional. If left blank, the time of the finalization will
* be used.
*
* A point or period of time associated with an event in the lifecycle of
* the resource.
*
* Date may be used to express temporal information at any level of
* granularity. Recommended best practice is to use an encoding scheme,
* such as the W3CDTF profile of ISO 8601 [W3CDTF].
*
* Used for the dc:date metadata parameter in the OPF file
*
* @param long $timestamp
* @access public
* @return bool $success
*/
function setDate($timestamp) {
if ($this->isFinalized) {
return FALSE;
}
$this->date = $timestamp;
$this->opf->date = $timestamp;
return TRUE;
}
/**
* Get Book date.
*
* @access public
* @return $date
*/
function getDate() {
return $this->date;
}
/**
* Book (copy)rights, optional.
*
* Information about rights held in and over the resource.
*
* Typically, rights information includes a statement about various
* property rights associated with the resource, including intellectual
* property rights.
*
* Used for the dc:rights metadata parameter in the OPF file
*
* @param string $rightsText
* @access public
* @return bool $success
*/
function setRights($rightsText) {
if ($this->isFinalized) {
return FALSE;
}
$this->rights = $rightsText;
return TRUE;
}
/**
* Get Book rights.
*
* @access public
* @return $rights
*/
function getRights() {
return $this->rights;
}
/**
* Add book Subject.
*
* The topic of the resource.
*
* Typically, the subject will be represented using keywords, key phrases,
* or classification codes. Recommended best practice is to use a
* controlled vocabulary. To describe the spatial or temporal topic of the
* resource, use the Coverage element.
*
* @param string $subject
*/
function setSubject($subject) {
if ($this->isFinalized) {
return;
}
$this->opf->addDCMeta(DublinCore::SUBJECT, $this->decodeHtmlEntities($subject));
}
/**
* Book source URL, optional.
*
* A related resource from which the described resource is derived.
*
* The described resource may be derived from the related resource in whole
* or in part. Recommended best practice is to identify the related
* resource by means of a string conforming to a formal identification system.
*
* Used for the dc:source metadata parameter in the OPF file
*
* @param string $sourceURL
* @access public
* @return bool $success
*/
function setSourceURL($sourceURL) {
if ($this->isFinalized) {
return FALSE;
}
$this->sourceURL = $sourceURL;
return TRUE;
}
/**
* Get Book sourceURL.
*
* @access public
* @return $sourceURL
*/
function getSourceURL() {
return $this->sourceURL;
}
/**
* Coverage, optional.
*
* The spatial or temporal topic of the resource, the spatial applicability
* of the resource, or the jurisdiction under which the resource is relevant.
*
* Spatial topic and spatial applicability may be a named place or a location
* specified by its geographic coordinates. Temporal topic may be a named
* period, date, or date range. A jurisdiction may be a named administrative
* entity or a geographic place to which the resource applies. Recommended
* best practice is to use a controlled vocabulary such as the Thesaurus of
* Geographic Names [TGN]. Where appropriate, named places or time periods
* can be used in preference to numeric identifiers such as sets of
* coordinates or date ranges.
*
* Used for the dc:coverage metadata parameter in the OPF file
*
* Same as ->addDublinCoreMetadata(DublinCore::COVERAGE, $coverage);
*
* @param string $coverage
* @access public
* @return bool $success
*/
function setCoverage($coverage) {
if ($this->isFinalized) {
return FALSE;
}
$this->coverage = $coverage;
return TRUE;
}
/**
* Get Book coverage.
*
* @access public
* @return $coverage
*/
function getCoverage() {
return $this->coverage;
}
/**
* Set book Relation.
*
* A related resource.
*
* Recommended best practice is to identify the related resource by means
* of a string conforming to a formal identification system.
*
* @param string $relation
*/
function setRelation($relation) {
if ($this->isFinalized) {
return;
}
$this->relation = $relation;
}
/**
* Get the book relation.
*
* @return string The relation.
*/
function getRelation() {
return $this->relation;
}
/**
* Set book Generator.
*
* The generator is a meta tag added to the ncx file, it is not visible
* from within the book, but is a kind of electronic watermark.
*
* @param string $generator
*/
function setGenerator($generator) {
if ($this->isFinalized) {
return;
}
$this->generator = $generator;
}
/**
* Get the book relation.
*
* @return string The generator identity string.
*/
function getGenerator() {
return $this->generator;
}
/**
* Set ePub date formate to the short yyyy-mm-dd form, for compliance with
* a bug in EpubCheck, prior to its version 1.1.
*
* The latest version of ePubCheck can be obtained here:
* http://code.google.com/p/epubcheck/
*
* @access public
* @return bool $success
*/
function setShortDateFormat() {
if ($this->isFinalized) {
return FALSE;
}
$this->dateformat = $this->dateformatShort;
return TRUE;
}
/**
* @Deprecated
*/
function setIgnoreEmptyBuffer($ignoreEmptyBuffer = TRUE) {
die ("Function was deprecated, functionality is no longer needed.");
}
/**
* Set the references title for the ePub 3 landmarks section
*
* @param string $referencesTitle
* @param string $referencesId
* @param string $referencesClass
* @return bool
*/
function setReferencesTitle($referencesTitle = "Guide", $referencesId = "", $referencesClass = "references") {
if ($this->isFinalized) {
return FALSE;
}
$this->ncx->referencesTitle = is_string($referencesTitle) ? trim($referencesTitle) : "Guide";
$this->ncx->referencesId = is_string($referencesId) ? trim($referencesId) : "references";
$this->ncx->referencesClass = is_string($referencesClass) ? trim($referencesClass) : "references";
return TRUE;
}
/**
* Set the references title for the ePub 3 landmarks section
*
* @param bool $referencesTitle
*/
function setisReferencesAddedToToc($isReferencesAddedToToc = TRUE) {
if ($this->isFinalized) {
return FALSE;
}
$this->isReferencesAddedToToc = $isReferencesAddedToToc === TRUE;
return TRUE;
}
/**
* Get Book status.
*
* @access public
* @return bool
*/
function isFinalized() {
return $this->isFinalized;
}
/**
* Build the Table of Contents. This is not strictly necessary, as most eReaders will build it from the navigation structure in the .ncx file.
*
* @param string $cssFileName Include a link to this css file in the TOC html.
* @param string $tocCSSClass The TOC is a " . str_repeat(" ", $level) . "sanitizeFileName($fileName) . "\">" . $chapterName . "
\n"; } } else if ($this->tocAddReferences === TRUE) { if (array_key_exists($item, $this->ncx->referencesList)) { $tocData .= "\t\n"; } else if ($item === "toc") { $tocData .= "\t\n"; } else if ($item === "cover" && $this->isCoverImageSet) { $tocData .= "\t\n"; } } } $tocData .= "