]> git.immae.eu Git - github/wallabag/wallabag.git/blame - inc/3rdparty/libraries/PHPePub/EPub.php
Fixed a bug into PHPePub with special caracters
[github/wallabag/wallabag.git] / inc / 3rdparty / libraries / PHPePub / EPub.php
CommitLineData
87090d8a 1<?php
2/**
3 * Create an ePub compatible book file.
4 *
5 * Please note, once finalized a book can no longer have chapters of data added or changed.
6 *
7 * License: GNU LGPL, Attribution required for commercial implementations, requested for everything else.
8 *
9 * Thanks to: Adam Schmalhofer and Kirstyn Fox for invaluable input and for "nudging" me in the right direction :)
10 *
11 * @author A. Grandt <php@grandt.com>
12 * @copyright 2009-2014 A. Grandt
13 * @license GNU LGPL 2.1
14 * @version 3.20
15 * @link http://www.phpclasses.org/package/6115
16 * @link https://github.com/Grandt/PHPePub
17 * @uses Zip.php version 1.50; http://www.phpclasses.org/browse/package/6110.html or https://github.com/Grandt/PHPZip
18 */
19class EPub {
20 const VERSION = 3.20;
21 const REQ_ZIP_VERSION = 1.60;
22
23 const IDENTIFIER_UUID = 'UUID';
24 const IDENTIFIER_URI = 'URI';
25 const IDENTIFIER_ISBN = 'ISBN';
26
27 /** Ignore all external references, and do not process the file for these */
28 const EXTERNAL_REF_IGNORE = 0;
29 /** Process the file for external references and add them to the book */
30 const EXTERNAL_REF_ADD = 1;
31 /** Process the file for external references and add them to the book, but remove images, and img tags */
32 const EXTERNAL_REF_REMOVE_IMAGES = 2;
33 /** Process the file for external references and add them to the book, but replace images, and img tags with [image] */
34 const EXTERNAL_REF_REPLACE_IMAGES = 3;
35
36 const DIRECTION_LEFT_TO_RIGHT = "ltr";
37 const DIRECTION_RIGHT_TO_LEFT = "rtl";
38
39 const BOOK_VERSION_EPUB2 = "2.0";
40 const BOOK_VERSION_EPUB3 = "3.0";
41
42 private $bookVersion = EPub::BOOK_VERSION_EPUB2;
43
44 public $maxImageWidth = 768;
45 public $maxImageHeight = 1024;
46
47 public $splitDefaultSize = 250000;
48 /** Gifs can crash some early ADE based readers, and are disabled by default.
49 * getImage will convert these if it can, unless this is set to TRUE.
50 */
51 public $isGifImagesEnabled = FALSE;
52 public $isReferencesAddedToToc = TRUE;
53
54 private $zip;
55
56 private $title = "";
57 private $language = "en";
58 private $identifier = "";
59 private $identifierType = "";
60 private $description = "";
61 private $author = "";
62 private $authorSortKey = "";
63 private $publisherName = "";
64 private $publisherURL = "";
65 private $date = 0;
66 private $rights = "";
67 private $coverage = "";
68 private $relation = "";
69 private $sourceURL = "";
70
71 private $chapterCount = 0;
72 private $opf = NULL;
73 private $ncx = NULL;
74 private $isFinalized = FALSE;
75 private $isCoverImageSet = FALSE;
76 private $buildTOC = FALSE;
77 private $tocTitle = NULL;
78 private $tocFileName = NULL;
79 private $tocCSSClass = NULL;
80 private $tocAddReferences = FALSE;
81 private $tocCssFileName = NULL;
82
83 private $fileList = array();
84 private $writingDirection = EPub::DIRECTION_LEFT_TO_RIGHT;
85 private $languageCode = "en";
86
87 /**
88 * Used for building the TOC.
89 * If this list is overwritten it MUST contain at least "text" as an element.
90 */
91 public $referencesOrder = NULL;
92
93 private $dateformat = 'Y-m-d\TH:i:s.000000P'; // ISO 8601 long
94 private $dateformatShort = 'Y-m-d'; // short date format to placate ePubChecker.
95 private $headerDateFormat = "D, d M Y H:i:s T";
96
97 protected $isCurlInstalled;
98 protected $isGdInstalled;
99 protected $isExifInstalled;
100 protected $isFileGetContentsInstalled;
101 protected $isFileGetContentsExtInstalled;
102
103 private $bookRoot = "OEBPS/";
104 private $docRoot = NULL;
105 private $EPubMark = TRUE;
106 private $generator = "";
107
108 private $log = NULL;
109 public $isLogging = TRUE;
110
111 public $encodeHTML = FALSE;
112
113 private $mimetypes = array(
114 "js" => "application/x-javascript", "swf" => "application/x-shockwave-flash", "xht" => "application/xhtml+xml", "xhtml" => "application/xhtml+xml", "zip" => "application/zip",
115 "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",
116 "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",
117 "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",
118 "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");
119
120 // 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.
121 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");
122
123 private $opsContentTypes = array("application/xhtml+xml", "application/x-dtbook+xml", "application/xml", "application/x-dtbncx+xml", "text/x-oeb1-document");
124
125 private $forbiddenCharacters = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%");
126
127 private $htmlContentHeader = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n<title></title>\n</head>\n<body>\n";
128 private $htmlContentFooter = "</body>\n</html>\n";
129
130 /**
131 * Class constructor.
132 *
133 * @return void
134 */
135 function __construct($bookVersion = EPub::BOOK_VERSION_EPUB2, $languageCode = "en", $writingDirection = EPub::DIRECTION_LEFT_TO_RIGHT) {
136 include_once("Zip.php");
137 include_once("Logger.php");
138
139 $this->bookVersion = $bookVersion;
140 $this->writingDirection = $writingDirection;
141 $this->languageCode = $languageCode;
142
143 $this->log = new Logger("EPub", $this->isLogging);
144
145 /* Prepare Logging. Just in case it's used. later */
146 if ($this->isLogging) {
147 $this->log->logLine("EPub class version....: " . self::VERSION);
148 $this->log->logLine("EPub req. Zip version.: " . self::REQ_ZIP_VERSION);
149 $this->log->logLine("Zip version...........: " . Zip::VERSION);
150 $this->log->dumpInstalledModules();
151 }
152
153 if (!defined("Zip::VERSION") || Zip::VERSION < self::REQ_ZIP_VERSION) {
154 die("<p>EPub version " . self::VERSION . " requires Zip.php at version " . self::REQ_ZIP_VERSION . " or higher.<br />You can obtain the latest version from <a href=\"http://www.phpclasses.org/browse/package/6110.html\">http://www.phpclasses.org/browse/package/6110.html</a>.</p>");
155 }
156
157 include_once("EPubChapterSplitter.php");
158 include_once("EPub.HtmlEntities.php");
159 include_once("EPub.NCX.php");
160 include_once("EPub.OPF.php");
161
162 $this->initialize();
163 }
164
165 /**
166 * Class destructor
167 *
168 * @return void
169 * @TODO make sure elements in the destructor match the current class elements
170 */
171 function __destruct() {
172 unset($this->bookVersion, $this->maxImageWidth, $this->maxImageHeight);
173 unset($this->splitDefaultSize, $this->isGifImagesEnabled, $this->isReferencesAddedToToc);
174 unset($this->zip, $this->title, $this->language, $this->identifier, $this->identifierType);
175 unset($this->description, $this->author, $this->authorSortKey, $this->publisherName);
176 unset($this->publisherURL, $this->date, $this->rights, $this->coverage, $this->relation);
177 unset($this->sourceURL, $this->chapterCount, $this->opf, $this->ncx, $this->isFinalized);
178 unset($this->isCoverImageSet, $this->fileList, $this->writingDirection, $this->languageCode);
179 unset($this->referencesOrder, $this->dateformat, $this->dateformatShort, $this->headerDateFormat);
180 unset($this->isCurlInstalled, $this->isGdInstalled, $this->isExifInstalled);
181 unset($this->isFileGetContentsInstalled, $this->isFileGetContentsExtInstalled, $this->bookRoot);
182 unset($this->docRoot, $this->EPubMark, $this->generator, $this->log, $this->isLogging);
183 unset($this->encodeHTML, $this->mimetypes, $this->coreMediaTypes, $this->opsContentTypes);
184 unset($this->forbiddenCharacters, $this->htmlContentHeader, $this->htmlContentFooter);
185 unset($this->buildTOC, $this->tocTitle, $this->tocCSSClass, $this->tocAddReferences);
186 unset($this->tocFileName, $this->tocCssFileName);
187 }
188
189 /**
190 * initialize defaults.
191 */
192 private function initialize() {
193 $this->referencesOrder = array(
194 Reference::COVER => "Cover Page",
195 Reference::TITLE_PAGE => "Title Page",
196 Reference::ACKNOWLEDGEMENTS => "Acknowledgements",
197 Reference::BIBLIOGRAPHY => "Bibliography",
198 Reference::COLOPHON => "Colophon",
199 Reference::COPYRIGHT_PAGE => "Copyright",
200 Reference::DEDICATION => "Dedication",
201 Reference::EPIGRAPH => "Epigraph",
202 Reference::FOREWORD => "Foreword",
203 Reference::TABLE_OF_CONTENTS => "Table of Contents",
204 Reference::NOTES => "Notes",
205 Reference::PREFACE => "Preface",
206 Reference::TEXT => "First Page",
207 Reference::LIST_OF_ILLUSTRATIONS => "List of Illustrations",
208 Reference::LIST_OF_TABLES => "List of Tables",
209 Reference::GLOSSARY => "Glossary",
210 Reference::INDEX => "Index");
211
212 $this->docRoot = filter_input(INPUT_SERVER, "DOCUMENT_ROOT") . "/";
213
214 $this->isCurlInstalled = extension_loaded('curl') && function_exists('curl_version');
215 $this->isGdInstalled = extension_loaded('gd') && function_exists('gd_info');
216 $this->isExifInstalled = extension_loaded('exif') && function_exists('exif_imagetype');
217 $this->isFileGetContentsInstalled = function_exists('file_get_contents');
218 $this->isFileGetContentsExtInstalled = $this->isFileGetContentsInstalled && ini_get('allow_url_fopen');
219
220 $this->zip = new Zip();
221 $this->zip->setExtraField(FALSE);
222 $this->zip->addFile("application/epub+zip", "mimetype");
223 $this->zip->setExtraField(TRUE);
224 $this->zip->addDirectory("META-INF");
225
226 $this->content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<container version=\"1.0\" xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\">\n\t<rootfiles>\n\t\t<rootfile full-path=\"" . $this->bookRoot . "book.opf\" media-type=\"application/oebps-package+xml\" />\n\t</rootfiles>\n</container>\n";
227
228 if (!$this->isEPubVersion2()) {
229 $this->htmlContentHeader = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
230 . "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n"
231 . "<head>"
232 . "<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n"
233 . "<title></title>\n"
234 . "</head>\n"
235 . "<body>\n";
236 }
237
238 $this->zip->addFile($this->content, "META-INF/container.xml", 0, NULL, FALSE);
239 $this->content = NULL;
240 $this->ncx = new Ncx(NULL, NULL, NULL, $this->languageCode, $this->writingDirection);
241 $this->opf = new Opf();
242 $this->ncx->setVersion($this->bookVersion);
243 $this->opf->setVersion($this->bookVersion);
244 $this->opf->addItem("ncx", "book.ncx", Ncx::MIMETYPE);
245 $this->chapterCount = 0;
246 }
247
248 /**
249 * Add dynamically generated data as a file to the book.
250 *
251 * @param string $fileName Filename to use for the file, must be unique for the book.
252 * @param string $fileId Unique identifier for the file.
253 * @param string $fileData File data
254 * @param string $mimetype file mime type
255 * @return bool $success
256 */
257 function addFile($fileName, $fileId, $fileData, $mimetype) {
258 if ($this->isFinalized || array_key_exists($fileName, $this->fileList)) {
259 return FALSE;
260 }
261
262 $fileName = $this->normalizeFileName($fileName);
263
264 $compress = (strpos($mimetype, "image/") !== 0);
265
266 $this->zip->addFile($fileData, $this->bookRoot.$fileName, 0, NULL, $compress);
267 $this->fileList[$fileName] = $fileName;
268 $this->opf->addItem($fileId, $fileName, $mimetype);
269 return TRUE;
270 }
271
272 /**
273 * Add a large file directly from the filestystem to the book.
274 *
275 * @param string $fileName Filename to use for the file, must be unique for the book.
276 * @param string $fileId Unique identifier for the file.
277 * @param string $filePath File path
278 * @param string $mimetype file mime type
279 * @return bool $success
280 */
281 function addLargeFile($fileName, $fileId, $filePath, $mimetype) {
282 if ($this->isFinalized || array_key_exists($fileName, $this->fileList)) {
283 return FALSE;
284 }
285 $fileName = $this->normalizeFileName($fileName);
286
287 if ($this->zip->addLargeFile($filePath, $this->bookRoot.$fileName)) {
288 $this->fileList[$fileName] = $fileName;
289 $this->opf->addItem($fileId, $fileName, $mimetype);
290 return TRUE;
291 }
292 return FALSE;
293 }
294
295 /**
296 * Add a CSS file to the book.
297 *
298 * @param string $fileName Filename to use for the CSS file, must be unique for the book.
299 * @param string $fileId Unique identifier for the file.
300 * @param string $fileData CSS data
301 * @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 <code>processCSSExternalReferences</code> for explanation. Default is EPub::EXTERNAL_REF_IGNORE.
302 * @param string $baseDir Default is "", meaning it is pointing to the document root. NOT used if $externalReferences is set to EPub::EXTERNAL_REF_IGNORE.
303 *
304 * @return bool $success
305 */
306 function addCSSFile($fileName, $fileId, $fileData, $externalReferences = EPub::EXTERNAL_REF_IGNORE, $baseDir = "") {
307 if ($this->isFinalized || array_key_exists($fileName, $this->fileList)) {
308 return FALSE;
309 }
310 $fileName = Zip::getRelativePath($fileName);
311 $fileName = preg_replace('#^[/\.]+#i', "", $fileName);
312
313 if ($externalReferences !== EPub::EXTERNAL_REF_IGNORE) {
314 $cssDir = pathinfo($fileName);
315 $cssDir = preg_replace('#^[/\.]+#i', "", $cssDir["dirname"] . "/");
316 if (!empty($cssDir)) {
317 $cssDir = preg_replace('#[^/]+/#i', "../", $cssDir);
318 }
319
320 $this->processCSSExternalReferences($fileData, $externalReferences, $baseDir, $cssDir);
321 }
322
323 $this->addFile($fileName, "css_" . $fileId, $fileData, "text/css");
324
325 return TRUE;
326 }
327
328 /**
329 * Add a chapter to the book, as a chapter should not exceed 250kB, you can parse an array with multiple parts as $chapterData.
330 * These will still only show up as a single chapter in the book TOC.
331 *
332 * @param string $chapterName Name of the chapter, will be use din the TOC
333 * @param string $fileName Filename to use for the chapter, must be unique for the book.
334 * @param string $chapter Chapter text in XHTML or array $chapterData valid XHTML data for the chapter. File should NOT exceed 250kB.
335 * @param bool $autoSplit Should the chapter be split if it exceeds the default split size? Default=FALSE, only used if $chapterData is a string.
336 * @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 <code>processChapterExternalReferences</code> for explanation. Default is EPub::EXTERNAL_REF_IGNORE.
337 * @param string $baseDir Default is "", meaning it is pointing to the document root. NOT used if $externalReferences is set to EPub::EXTERNAL_REF_IGNORE.
338 * @return mixed $success FALSE if the addition failed, else the new NavPoint.
339 */
340 function addChapter($chapterName, $fileName, $chapterData = NULL, $autoSplit = FALSE, $externalReferences = EPub::EXTERNAL_REF_IGNORE, $baseDir = "") {
341 if ($this->isFinalized) {
342 return FALSE;
343 }
344 $fileName = Zip::getRelativePath($fileName);
345 $fileName = preg_replace('#^[/\.]+#i', "", $fileName);
72a85715 346 $fileName = $this->sanitizeFileName($fileName);
87090d8a 347
348 $chapter = $chapterData;
349 if ($autoSplit && is_string($chapterData) && mb_strlen($chapterData) > $this->splitDefaultSize) {
350 $splitter = new EPubChapterSplitter();
351
352 $chapterArray = $splitter->splitChapter($chapterData);
353 if (count($chapterArray) > 1) {
354 $chapter = $chapterArray;
355 }
356 }
357
358 if (!empty($chapter) && is_string($chapter)) {
359 if ($externalReferences !== EPub::EXTERNAL_REF_IGNORE) {
360 $htmlDirInfo = pathinfo($fileName);
361 $htmlDir = preg_replace('#^[/\.]+#i', "", $htmlDirInfo["dirname"] . "/");
362 $this->processChapterExternalReferences($chapter, $externalReferences, $baseDir, $htmlDir);
363 }
364
365 if ($this->encodeHTML === TRUE) {
366 $chapter = $this->encodeHtml($chapter);
367 }
368
369 $this->chapterCount++;
370 $this->addFile($fileName, "chapter" . $this->chapterCount, $chapter, "application/xhtml+xml");
371 $this->opf->addItemRef("chapter" . $this->chapterCount);
372
373 $navPoint = new NavPoint($this->decodeHtmlEntities($chapterName), $fileName, "chapter" . $this->chapterCount);
374 $this->ncx->addNavPoint($navPoint);
375 $this->ncx->chapterList[$chapterName] = $navPoint;
376 } else if (is_array($chapter)) {
377 $fileNameParts = pathinfo($fileName);
378 $extension = $fileNameParts['extension'];
379 $name = $fileNameParts['filename'];
380
381 $partCount = 0;
382 $this->chapterCount++;
383
384 $oneChapter = each($chapter);
385 while ($oneChapter) {
386 list($k, $v) = $oneChapter;
387 if ($this->encodeHTML === TRUE) {
388 $v = $this->encodeHtml($v);
389 }
390
391 if ($externalReferences !== EPub::EXTERNAL_REF_IGNORE) {
392 $this->processChapterExternalReferences($v, $externalReferences, $baseDir);
393 }
394 $partCount++;
395 $partName = $name . "_" . $partCount;
396 $this->addFile($partName . "." . $extension, $partName, $v, "application/xhtml+xml");
397 $this->opf->addItemRef($partName);
398
399 $oneChapter = each($chapter);
400 }
401 $partName = $name . "_1." . $extension;
402 $navPoint = new NavPoint($this->decodeHtmlEntities($chapterName), $partName, $partName);
403 $this->ncx->addNavPoint($navPoint);
404
405 $this->ncx->chapterList[$chapterName] = $navPoint;
406 } else if (!isset($chapterData) && strpos($fileName, "#") > 0) {
407 $this->chapterCount++;
408 //$this->opf->addItemRef("chapter" . $this->chapterCount);
409
410 $navPoint = new NavPoint($this->decodeHtmlEntities($chapterName), $fileName, "chapter" . $this->chapterCount);
411 $this->ncx->addNavPoint($navPoint);
412 $this->ncx->chapterList[$chapterName] = $navPoint;
413 } else if (!isset($chapterData) && $fileName=="TOC.xhtml") {
414 $this->chapterCount++;
415 $this->opf->addItemRef("toc");
416
417 $navPoint = new NavPoint($this->decodeHtmlEntities($chapterName), $fileName, "chapter" . $this->chapterCount);
418 $this->ncx->addNavPoint($navPoint);
419 $this->ncx->chapterList[$chapterName] = $navPoint;
420 }
421 return $navPoint;
422 }
423
424 /**
425 * Add one chapter level.
426 *
427 * Subsequent chapters will be added to this level.
428 *
429 * @param string $navTitle
430 * @param string $navId
431 * @param string $navClass
432 * @param int $isNavHidden
433 * @param string $writingDirection
434 * @return NavPoint The new NavPoint for that level.
435 */
436 function subLevel($navTitle = NULL, $navId = NULL, $navClass = NULL, $isNavHidden = FALSE, $writingDirection = NULL) {
437 return $this->ncx->subLevel($this->decodeHtmlEntities($navTitle), $navId, $navClass, $isNavHidden, $writingDirection);
438 }
439
440 /**
441 * Step back one chapter level.
442 *
443 * Subsequent chapters will be added to this chapters parent level.
444 */
445 function backLevel() {
446 $this->ncx->backLevel();
447 }
448
449 /**
450 * Step back to the root level.
451 *
452 * Subsequent chapters will be added to the rooot NavMap.
453 */
454 function rootLevel() {
455 $this->ncx->rootLevel();
456 }
457
458 /**
459 * Step back to the given level.
460 * Useful for returning to a previous level from deep within the structure.
461 * Values below 2 will have the same effect as rootLevel()
462 *
463 * @param int $newLevel
464 */
465 function setCurrentLevel($newLevel) {
466 $this->ncx->setCurrentLevel($newLevel);
467 }
468
469 /**
470 * Get current level count.
471 * The indentation of the current structure point.
472 *
473 * @return current level count;
474 */
475 function getCurrentLevel() {
476 return $this->ncx->getCurrentLevel();
477 }
478
479 /**
480 * Wrap ChapterContent with Head and Footer
481 *
482 * @param $content
483 * @return string $content
484 */
485 private function wrapChapter($content) {
486 return $this->htmlContentHeader . "\n" . $content . "\n" . $this->htmlContentFooter;
487 }
488
489 /**
490 * Reference pages is usually one or two pages for items such as Table of Contents, reference lists, Author notes or Acknowledgements.
491 * These do not show up in the regular navigation list.
492 *
493 * As they are supposed to be short.
494 *
495 * @param string $pageName Name of the chapter, will be use din the TOC
496 * @param string $fileName Filename to use for the chapter, must be unique for the book.
497 * @param string $pageData Page content in XHTML. File should NOT exceed 250kB.
498 * @param string $reference Reference key
499 * @param int $externalReferences How to handle external references. See documentation for <code>processChapterExternalReferences</code> for explanation. Default is EPub::EXTERNAL_REF_IGNORE.
500 * @param string $baseDir Default is "", meaning it is pointing to the document root. NOT used if $externalReferences is set to EPub::EXTERNAL_REF_IGNORE.
501 * @return bool $success
502 */
503 function addReferencePage($pageName, $fileName, $pageData, $reference, $externalReferences = EPub::EXTERNAL_REF_IGNORE, $baseDir = "") {
504 if ($this->isFinalized) {
505 return FALSE;
506 }
507 $fileName = Zip::getRelativePath($fileName);
508 $fileName = preg_replace('#^[/\.]+#i', "", $fileName);
509
510
511 if (!empty($pageData) && is_string($pageData)) {
512 if ($this->encodeHTML === TRUE) {
513 $pageData = $this->encodeHtml($pageData);
514 }
515
516 $this->wrapChapter($pageData);
517
518 if ($externalReferences !== EPub::EXTERNAL_REF_IGNORE) {
519 $htmlDirInfo = pathinfo($fileName);
520 $htmlDir = preg_replace('#^[/\.]+#i', "", $htmlDirInfo["dirname"] . "/");
521 $this->processChapterExternalReferences($pageData, $externalReferences, $baseDir, $htmlDir);
522 }
523
524 $this->addFile($fileName, "ref_" . $reference, $pageData, "application/xhtml+xml");
525
526 if ($reference !== Reference::TABLE_OF_CONTENTS || !isset($this->ncx->referencesList[$reference])) {
527 $this->opf->addItemRef("ref_" . $reference, FALSE);
528 $this->opf->addReference($reference, $pageName, $fileName);
529
530 $this->ncx->referencesList[$reference] = $fileName;
531 $this->ncx->referencesName[$reference] = $pageName;
532 }
533 return TRUE;
534 }
535 return TRUE;
536 }
537
538 /**
539 * Add custom metadata to the book.
540 *
541 * It is up to the builder to make sure there are no collisions. Metadata are just key value pairs.
542 *
543 * @param string $name
544 * @param string $content
545 */
546 function addCustomMetadata($name, $content) {
547 $this->opf->addMeta($name, $content);
548 }
549
550 /**
551 * Add DublinCore metadata to the book
552 *
553 * Use the DublinCore constants included in EPub, ie DublinCore::DATE
554 *
555 * @param string $dublinCore name
556 * @param string $value
557 */
558 function addDublinCoreMetadata($dublinCoreConstant, $value) {
559 if ($this->isFinalized) {
560 return;
561 }
562
563 $this->opf->addDCMeta($dublinCoreConstant, $this->decodeHtmlEntities($value));
564 }
565
566 /**
567 * Add a cover image to the book.
568 * If the $imageData is not set, the function assumes the $fileName is the path to the image file.
569 *
570 * The styling and structure of the generated XHTML is heavily inspired by the XHTML generated by Calibre.
571 *
572 * @param string $fileName Filename to use for the image, must be unique for the book.
573 * @param string $imageData Binary image data
574 * @param string $mimetype Image mimetype, such as "image/jpeg" or "image/png".
575 * @return bool $success
576 */
577 function setCoverImage($fileName, $imageData = NULL, $mimetype = NULL) {
578 if ($this->isFinalized || $this->isCoverImageSet || array_key_exists("CoverPage.html", $this->fileList)) {
579 return FALSE;
580 }
581
582 if ($imageData == NULL) {
583 // assume $fileName is the valid file path.
584 if (!file_exists($fileName)) {
585 // Attempt to locate the file using the doc root.
586 $rp = realpath($this->docRoot . "/" . $fileName);
587
588 if ($rp !== FALSE) {
589 // only assign the docroot path if it actually exists there.
590 $fileName = $rp;
591 }
592 }
593 $image = $this->getImage($fileName);
594 $imageData = $image['image'];
595 $mimetype = $image['mime'];
596 $fileName = preg_replace("#\.[^\.]+$#", "." . $image['ext'], $fileName);
597 }
598
599
600 $path = pathinfo($fileName);
601 $imgPath = "images/" . $path["basename"];
602
603 if (empty($mimetype) && file_exists($fileName)) {
604 list($width, $height, $type, $attr) = getimagesize($fileName);
605 $mimetype = image_type_to_mime_type($type);
606 }
607 if (empty($mimetype)) {
608 $ext = strtolower($path['extension']);
609 if ($ext == "jpg") {
610 $ext = "jpeg";
611 }
612 $mimetype = "image/" . $ext;
613 }
614
615 $coverPage = "";
616
617 if ($this->isEPubVersion2()) {
618 $coverPage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
619 . "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n"
620 . " \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
621 . "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\" xml:lang=\"en\">\n"
622 . "\t<head>\n"
623 . "\t\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n"
624 . "\t\t<title>Cover Image</title>\n"
625 . "\t\t<link type=\"text/css\" rel=\"stylesheet\" href=\"Styles/CoverPage.css\" />\n"
626 . "\t</head>\n"
627 . "\t<body>\n"
628 . "\t\t<div>\n"
629 . "\t\t\t<img src=\"" . $imgPath . "\" alt=\"Cover image\" style=\"height: 100%\"/>\n"
630 . "\t\t</div>\n"
631 . "\t</body>\n"
632 . "</html>\n";
633 } else {
634 $coverPage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
635 . "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n"
636 . "<head>"
637 . "\t<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n"
638 . "\t\t<title>Cover Image</title>\n"
639 . "\t\t<link type=\"text/css\" rel=\"stylesheet\" href=\"Styles/CoverPage.css\" />\n"
640 . "\t</head>\n"
641 . "\t<body>\n"
642 . "\t\t<section epub:type=\"cover\">\n"
643 . "\t\t\t<img src=\"" . $imgPath . "\" alt=\"Cover image\" style=\"height: 100%\"/>\n"
644 . "\t\t</section>\n"
645 . "\t</body>\n"
646 . "</html>\n";
647 }
648 $coverPageCss = "@page, body, div, img {\n"
649 . "\tpadding: 0pt;\n"
650 . "\tmargin:0pt;\n"
651 . "}\n\nbody {\n"
652 . "\ttext-align: center;\n"
653 . "}\n";
654
655 $this->addCSSFile("Styles/CoverPage.css", "CoverPageCss", $coverPageCss);
656 $this->addFile($imgPath, "CoverImage", $imageData, $mimetype);
657 $this->addReferencePage("CoverPage", "CoverPage.xhtml", $coverPage, "cover");
658 $this->isCoverImageSet = TRUE;
659 return TRUE;
660 }
661
662 /**
663 * Process external references from a HTML to the book. The chapter itself is not stored.
664 * the HTML is scanned for &lt;link..., &lt;style..., and &lt;img tags.
665 * Embedded CSS styles and links will also be processed.
666 * Script tags are not processed, as scripting should be avoided in e-books.
667 *
668 * EPub keeps track of added files, and duplicate files referenced across multiple
669 * chapters, are only added once.
670 *
671 * If the $doc is a string, it is assumed to be the content of an HTML file,
672 * else is it assumes to be a DOMDocument.
673 *
674 * Basedir is the root dir the HTML is supposed to "live" in, used to resolve
675 * relative references such as <code>&lt;img src="../images/image.png"/&gt;</code>
676 *
677 * $externalReferences determines how the function will handle external references.
678 *
679 * @param mixed &$doc (referenced)
680 * @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.
681 * @param string $baseDir Default is "", meaning it is pointing to the document root.
682 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
683 *
684 * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
685 */
686 protected function processChapterExternalReferences(&$doc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "") {
687 if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
688 return FALSE;
689 }
690
691 $backPath = preg_replace('#[^/]+/#i', "../", $htmlDir);
692 $isDocAString = is_string($doc);
693 $xmlDoc = NULL;
694
695 if ($isDocAString) {
696 $xmlDoc = new DOMDocument();
697 @$xmlDoc->loadHTML($doc);
698 } else {
699 $xmlDoc = $doc;
700 }
701
702 $this->processChapterStyles($xmlDoc, $externalReferences, $baseDir, $htmlDir);
703 $this->processChapterLinks($xmlDoc, $externalReferences, $baseDir, $htmlDir, $backPath);
704 $this->processChapterImages($xmlDoc, $externalReferences, $baseDir, $htmlDir, $backPath);
705 $this->processChapterSources($xmlDoc, $externalReferences, $baseDir, $htmlDir, $backPath);
706
707 if ($isDocAString) {
708 //$html = $xmlDoc->saveXML();
709
710 $htmlNode = $xmlDoc->getElementsByTagName("html");
711 $headNode = $xmlDoc->getElementsByTagName("head");
712 $bodyNode = $xmlDoc->getElementsByTagName("body");
713
714 $htmlNS = "";
715 for ($index = 0; $index < $htmlNode->item(0)->attributes->length; $index++) {
716 $nodeName = $htmlNode->item(0)->attributes->item($index)->nodeName;
717 $nodeValue = $htmlNode->item(0)->attributes->item($index)->nodeValue;
718
719 if ($nodeName != "xmlns") {
720 $htmlNS .= " $nodeName=\"$nodeValue\"";
721 }
722 }
723
724 $xml = new DOMDocument('1.0', "utf-8");
725 $xml->lookupPrefix("http://www.w3.org/1999/xhtml");
726 $xml->preserveWhiteSpace = FALSE;
727 $xml->formatOutput = TRUE;
728
729 $xml2Doc = new DOMDocument('1.0', "utf-8");
730 $xml2Doc->lookupPrefix("http://www.w3.org/1999/xhtml");
731 $xml2Doc->loadXML("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\"$htmlNS>\n</html>\n");
732 $html = $xml2Doc->getElementsByTagName("html")->item(0);
733 $html->appendChild($xml2Doc->importNode($headNode->item(0), TRUE));
734 $html->appendChild($xml2Doc->importNode($bodyNode->item(0), TRUE));
735
736 // force pretty printing and correct formatting, should not be needed, but it is.
737 $xml->loadXML($xml2Doc->saveXML());
738 $doc = $xml->saveXML();
739
740 if (!$this->isEPubVersion2()) {
741 $doc = preg_replace('#^\s*<!DOCTYPE\ .+?>\s*#im', '', $doc);
742 }
743 }
744 return TRUE;
745 }
746
747 /**
748 * Process images referenced from an CSS file to the book.
749 *
750 * $externalReferences determins how the function will handle external references.
751 *
752 * @param string &$cssFile (referenced)
753 * @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.
754 * @param string $baseDir Default is "", meaning it is pointing to the document root.
755 * @param string $cssDir The of the CSS file's directory from the root of the archive.
756 *
757 * @return bool FALSE if unsuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
758 */
759 protected function processCSSExternalReferences(&$cssFile, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $cssDir = "") {
760 if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
761 return FALSE;
762 }
763
764 $backPath = preg_replace('#[^/]+/#i', "../", $cssDir);
765 $imgs = null;
766 preg_match_all('#url\s*\([\'\"\s]*(.+?)[\'\"\s]*\)#im', $cssFile, $imgs, PREG_SET_ORDER);
767
768 $itemCount = count($imgs);
769 for ($idx = 0; $idx < $itemCount; $idx++) {
770 $img = $imgs[$idx];
771 if ($externalReferences === EPub::EXTERNAL_REF_REMOVE_IMAGES || $externalReferences === EPub::EXTERNAL_REF_REPLACE_IMAGES) {
772 $cssFile = str_replace($img[0], "", $cssFile);
773 } else {
774 $source = $img[1];
775
776 $pathData = pathinfo($source);
777 $internalSrc = $pathData['basename'];
778 $internalPath = "";
779 $isSourceExternal = FALSE;
780
781 if ($this->resolveImage($source, $internalPath, $internalSrc, $isSourceExternal, $baseDir, $cssDir, $backPath)) {
782 $cssFile = str_replace($img[0], "url('" . $backPath . $internalPath . "')", $cssFile);
783 } else if ($isSourceExternal) {
784 $cssFile = str_replace($img[0], "", $cssFile); // External image is missing
785 } // else do nothing, if the image is local, and missing, assume it's been generated.
786 }
787 }
788 return TRUE;
789 }
790
791 /**
792 * Process style tags in a DOMDocument. Styles will be passed as CSS files and reinserted into the document.
793 *
794 * @param DOMDocument &$xmlDoc (referenced)
795 * @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.
796 * @param string $baseDir Default is "", meaning it is pointing to the document root.
797 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
798 *
799 * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
800 */
801 protected function processChapterStyles(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "") {
802 if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
803 return FALSE;
804 }
805 // process inlined CSS styles in style tags.
806 $styles = $xmlDoc->getElementsByTagName("style");
807 $styleCount = $styles->length;
808 for ($styleIdx = 0; $styleIdx < $styleCount; $styleIdx++) {
809 $style = $styles->item($styleIdx);
810
811 $styleData = preg_replace('#[/\*\s]*\<\!\[CDATA\[[\s\*/]*#im', "", $style->nodeValue);
812 $styleData = preg_replace('#[/\*\s]*\]\]\>[\s\*/]*#im', "", $styleData);
813
814 $this->processCSSExternalReferences($styleData, $externalReferences, $baseDir, $htmlDir);
815 $style->nodeValue = "\n" . trim($styleData) . "\n";
816 }
817 return TRUE;
818 }
819
820 /**
821 * 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.
822 * Link types text/css will be passed as CSS files.
823 *
824 * @param DOMDocument &$xmlDoc (referenced)
825 * @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.
826 * @param string $baseDir Default is "", meaning it is pointing to the document root.
827 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
828 * @param string $backPath The path to get back to the root of the archive from $htmlDir.
829 *
830 * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
831 */
832 protected function processChapterLinks(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "", $backPath = "") {
833 if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
834 return FALSE;
835 }
836 // process link tags.
837 $links = $xmlDoc->getElementsByTagName("link");
838 $linkCount = $links->length;
839 for ($linkIdx = 0; $linkIdx < $linkCount; $linkIdx++) {
840 $link = $links->item($linkIdx);
841 $source = $link->attributes->getNamedItem("href")->nodeValue;
842 $sourceData = NULL;
843
844 $pathData = pathinfo($source);
845 $internalSrc = $pathData['basename'];
846
847 if (preg_match('#^(http|ftp)s?://#i', $source) == 1) {
848 $urlinfo = parse_url($source);
849
850 if (strpos($urlinfo['path'], $baseDir."/") !== FALSE) {
851 $internalSrc = substr($urlinfo['path'], strpos($urlinfo['path'], $baseDir."/") + strlen($baseDir) + 1);
852 }
853
854 @$sourceData = getFileContents($source);
855 } else if (strpos($source, "/") === 0) {
856 @$sourceData = file_get_contents($this->docRoot . $source);
857 } else {
858 @$sourceData = file_get_contents($this->docRoot . $baseDir . "/" . $source);
859 }
860
861 if (!empty($sourceData)) {
862 if (!array_key_exists($internalSrc, $this->fileList)) {
863 $mime = $link->attributes->getNamedItem("type")->nodeValue;
864 if (empty($mime)) {
865 $mime = "text/plain";
866 }
867 if ($mime == "text/css") {
868 $this->processCSSExternalReferences($sourceData, $externalReferences, $baseDir, $htmlDir);
869 $this->addCSSFile($internalSrc, $internalSrc, $sourceData, EPub::EXTERNAL_REF_IGNORE, $baseDir);
870 $link->setAttribute("href", $backPath . $internalSrc);
871 } else {
872 $this->addFile($internalSrc, $internalSrc, $sourceData, $mime);
873 }
874 $this->fileList[$internalSrc] = $source;
875 } else {
876 $link->setAttribute("href", $backPath . $internalSrc);
877 }
878 } // else do nothing, if the link is local, and missing, assume it's been generated.
879 }
880 return TRUE;
881 }
882
883 /**
884 * Process img tags in a DOMDocument.
885 * $externalReferences will determine what will happen to these images, and the img src will be rewritten accordingly.
886 *
887 * @param DOMDocument &$xmlDoc (referenced)
888 * @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.
889 * @param string $baseDir Default is "", meaning it is pointing to the document root.
890 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
891 * @param string $backPath The path to get back to the root of the archive from $htmlDir.
892 *
893 * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
894 */
895 protected function processChapterImages(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "", $backPath = "") {
896 if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
897 return FALSE;
898 }
899 // process img tags.
900 $postProcDomElememts = array();
901 $images = $xmlDoc->getElementsByTagName("img");
902 $itemCount = $images->length;
903
904 for ($idx = 0; $idx < $itemCount; $idx++) {
905 $img = $images->item($idx);
906
907 if ($externalReferences === EPub::EXTERNAL_REF_REMOVE_IMAGES) {
908 $postProcDomElememts[] = $img;
909 } else if ($externalReferences === EPub::EXTERNAL_REF_REPLACE_IMAGES) {
910 $altNode = $img->attributes->getNamedItem("alt");
911 $alt = "image";
912 if ($altNode !== NULL && strlen($altNode->nodeValue) > 0) {
913 $alt = $altNode->nodeValue;
914 }
915 $postProcDomElememts[] = array($img, $this->createDomFragment($xmlDoc, "<em>[" . $alt . "]</em>"));
916 } else {
917 $source = $img->attributes->getNamedItem("src")->nodeValue;
918
919 $parsedSource = parse_url($source);
920 $internalSrc = $this->sanitizeFileName(urldecode(pathinfo($parsedSource['path'], PATHINFO_BASENAME)));
921 $internalPath = "";
922 $isSourceExternal = FALSE;
923
924 if ($this->resolveImage($source, $internalPath, $internalSrc, $isSourceExternal, $baseDir, $htmlDir, $backPath)) {
925 $img->setAttribute("src", $backPath . $internalPath);
926 } else if ($isSourceExternal) {
927 $postProcDomElememts[] = $img; // External image is missing
928 } // else do nothing, if the image is local, and missing, assume it's been generated.
929 }
930 }
931
932 foreach ($postProcDomElememts as $target) {
933 if (is_array($target)) {
934 $target[0]->parentNode->replaceChild($target[1], $target[0]);
935 } else {
936 $target->parentNode->removeChild($target);
937 }
938 }
939 return TRUE;
940 }
941
942 /**
943 * Process source tags in a DOMDocument.
944 * $externalReferences will determine what will happen to these images, and the img src will be rewritten accordingly.
945 *
946 * @param DOMDocument &$xmlDoc (referenced)
947 * @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.
948 * @param string $baseDir Default is "", meaning it is pointing to the document root.
949 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
950 * @param string $backPath The path to get back to the root of the archive from $htmlDir.
951 *
952 * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
953 */
954 protected function processChapterSources(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "", $backPath = "") {
955 if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
956 return FALSE;
957 }
958
959 if ($this->bookVersion !== EPub::BOOK_VERSION_EPUB3) {
960 // ePub 2 does not support multimedia formats, and they must be removed.
961 $externalReferences = EPub::EXTERNAL_REF_REMOVE_IMAGES;
962 }
963
964 $postProcDomElememts = array();
965 $images = $xmlDoc->getElementsByTagName("source");
966 $itemCount = $images->length;
967 for ($idx = 0; $idx < $itemCount; $idx++) {
968 $img = $images->item($idx);
969 if ($externalReferences === EPub::EXTERNAL_REF_REMOVE_IMAGES) {
970 $postProcDomElememts[] = $img;
971 } else if ($externalReferences === EPub::EXTERNAL_REF_REPLACE_IMAGES) {
972 $altNode = $img->attributes->getNamedItem("alt");
973 $alt = "image";
974 if ($altNode !== NULL && strlen($altNode->nodeValue) > 0) {
975 $alt = $altNode->nodeValue;
976 }
977 $postProcDomElememts[] = array($img, $this->createDomFragment($xmlDoc, "[" . $alt . "]"));
978 } else {
979 $source = $img->attributes->getNamedItem("src")->nodeValue;
980
981 $parsedSource = parse_url($source);
982 $internalSrc = $this->sanitizeFileName(urldecode(pathinfo($parsedSource['path'], PATHINFO_BASENAME)));
983 $internalPath = "";
984 $isSourceExternal = FALSE;
985
986 if ($this->resolveMedia($source, $internalPath, $internalSrc, $isSourceExternal, $baseDir, $htmlDir, $backPath)) {
987 $img->setAttribute("src", $backPath . $internalPath);
988 } else if ($isSourceExternal) {
989 $postProcDomElememts[] = $img; // External image is missing
990 } // else do nothing, if the image is local, and missing, assume it's been generated.
991 }
992 }
993 }
994
995 /**
996 * Resolve an image src and determine it's target location and add it to the book.
997 *
998 * @param string $source Image Source link.
999 * @param string &$internalPath (referenced) Return value, will be set to the target path and name in the book.
1000 * @param string &$internalSrc (referenced) Return value, will be set to the target name in the book.
1001 * @param string &$isSourceExternal (referenced) Return value, will be set to TRUE if the image originated from a full URL.
1002 * @param string $baseDir Default is "", meaning it is pointing to the document root.
1003 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
1004 * @param string $backPath The path to get back to the root of the archive from $htmlDir.
1005 */
1006 protected function resolveImage($source, &$internalPath, &$internalSrc, &$isSourceExternal, $baseDir = "", $htmlDir = "", $backPath = "") {
1007 if ($this->isFinalized) {
1008 return FALSE;
1009 }
1010 $imageData = NULL;
1011
1012 if (preg_match('#^(http|ftp)s?://#i', $source) == 1) {
1013 $urlinfo = parse_url($source);
1014 $urlPath = pathinfo($urlinfo['path']);
1015
1016 if (strpos($urlinfo['path'], $baseDir."/") !== FALSE) {
1017 $internalSrc = $this->sanitizeFileName(urldecode(substr($urlinfo['path'], strpos($urlinfo['path'], $baseDir."/") + strlen($baseDir) + 1)));
1018 }
1019 $internalPath = $urlinfo["scheme"] . "/" . $urlinfo["host"] . "/" . pathinfo($urlinfo["path"], PATHINFO_DIRNAME);
1020 $isSourceExternal = TRUE;
1021 $imageData = $this->getImage($source);
1022 } else if (strpos($source, "/") === 0) {
1023 $internalPath = pathinfo($source, PATHINFO_DIRNAME);
1024
1025 $path = $source;
1026 if (!file_exists($path)) {
1027 $path = $this->docRoot . $path;
1028 }
1029
1030 $imageData = $this->getImage($path);
1031 } else {
1032 $internalPath = $htmlDir . "/" . preg_replace('#^[/\.]+#', '', pathinfo($source, PATHINFO_DIRNAME));
1033
1034 $path = $baseDir . "/" . $source;
1035 if (!file_exists($path)) {
1036 $path = $this->docRoot . $path;
1037 }
1038
1039 $imageData = $this->getImage($path);
1040 }
1041 if ($imageData !== FALSE) {
1042 $iSrcInfo = pathinfo($internalSrc);
1043 if (!empty($imageData['ext']) && $imageData['ext'] != $iSrcInfo['extension']) {
1044 $internalSrc = $iSrcInfo['filename'] . "." . $imageData['ext'];
1045 }
1046 $internalPath = Zip::getRelativePath("images/" . $internalPath . "/" . $internalSrc);
1047 if (!array_key_exists($internalPath, $this->fileList)) {
1048 $this->addFile($internalPath, "i_" . $internalSrc, $imageData['image'], $imageData['mime']);
1049 $this->fileList[$internalPath] = $source;
1050 }
1051 return TRUE;
1052 }
1053 return FALSE;
1054 }
1055
1056 /**
1057 * Resolve a media src and determine it's target location and add it to the book.
1058 *
1059 * @param string $source Source link.
1060 * @param string $internalPath (referenced) Return value, will be set to the target path and name in the book.
1061 * @param string $internalSrc (referenced) Return value, will be set to the target name in the book.
1062 * @param string $isSourceExternal (referenced) Return value, will be set to TRUE if the image originated from a full URL.
1063 * @param string $baseDir Default is "", meaning it is pointing to the document root.
1064 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
1065 * @param string $backPath The path to get back to the root of the archive from $htmlDir.
1066 */
1067 protected function resolveMedia($source, &$internalPath, &$internalSrc, &$isSourceExternal, $baseDir = "", $htmlDir = "", $backPath = "") {
1068 if ($this->isFinalized) {
1069 return FALSE;
1070 }
1071 $mediaPath = NULL;
1072 $tmpFile;
1073
1074 if (preg_match('#^(http|ftp)s?://#i', $source) == 1) {
1075 $urlinfo = parse_url($source);
1076
1077 if (strpos($urlinfo['path'], $baseDir."/") !== FALSE) {
1078 $internalSrc = substr($urlinfo['path'], strpos($urlinfo['path'], $baseDir."/") + strlen($baseDir) + 1);
1079 }
1080 $internalPath = $urlinfo["scheme"] . "/" . $urlinfo["host"] . "/" . pathinfo($urlinfo["path"], PATHINFO_DIRNAME);
1081 $isSourceExternal = TRUE;
1082 $mediaPath = $this->getFileContents($source, true);
1083 $tmpFile = $mediaPath;
1084 } else if (strpos($source, "/") === 0) {
1085 $internalPath = pathinfo($source, PATHINFO_DIRNAME);
1086
1087 $mediaPath = $source;
1088 if (!file_exists($mediaPath)) {
1089 $mediaPath = $this->docRoot . $mediaPath;
1090 }
1091 } else {
1092 $internalPath = $htmlDir . "/" . preg_replace('#^[/\.]+#', '', pathinfo($source, PATHINFO_DIRNAME));
1093
1094 $mediaPath = $baseDir . "/" . $source;
1095 if (!file_exists($mediaPath)) {
1096 $mediaPath = $this->docRoot . $mediaPath;
1097 }
1098 }
1099
1100 if ($mediaPath !== FALSE) {
1101 $mime = $this->getMime($source);
1102 $internalPath = Zip::getRelativePath("media/" . $internalPath . "/" . $internalSrc);
1103
1104 if (!array_key_exists($internalPath, $this->fileList) &&
1105 $this->addLargeFile($internalPath, "m_" . $internalSrc, $mediaPath, $mime)) {
1106 $this->fileList[$internalPath] = $source;
1107 }
1108 if (isset($tmpFile)) {
1109 unlink($tmpFile);
1110 }
1111 return TRUE;
1112 }
1113 return FALSE;
1114 }
1115
1116 /**
1117 * Get Book Chapter count.
1118 *
1119 * @access public
1120 * @return number of chapters
1121 */
1122 function getChapterCount() {
1123 return $this->chapterCount;
1124 }
1125
1126 /**
1127 * Book title, mandatory.
1128 *
1129 * Used for the dc:title metadata parameter in the OPF file as well as the DocTitle attribute in the NCX file.
1130 *
1131 * @param string $title
1132 * @access public
1133 * @return bool $success
1134 */
1135 function setTitle($title) {
1136 if ($this->isFinalized) {
1137 return FALSE;
1138 }
1139 $this->title = $title;
1140 return TRUE;
1141 }
1142
1143 /**
1144 * Get Book title.
1145 *
1146 * @access public
1147 * @return $title
1148 */
1149 function getTitle() {
1150 return $this->title;
1151 }
1152
1153 /**
1154 * Book language, mandatory
1155 *
1156 * Use the RFC3066 Language codes, such as "en", "da", "fr" etc.
1157 * Defaults to "en".
1158 *
1159 * Used for the dc:language metadata parameter in the OPF file.
1160 *
1161 * @param string $language
1162 * @access public
1163 * @return bool $success
1164 */
1165 function setLanguage($language) {
1166 if ($this->isFinalized || mb_strlen($language) != 2) {
1167 return FALSE;
1168 }
1169 $this->language = $language;
1170 return TRUE;
1171 }
1172
1173 /**
1174 * Get Book language.
1175 *
1176 * @access public
1177 * @return $language
1178 */
1179 function getLanguage() {
1180 return $this->language;
1181 }
1182
1183 /**
1184 * Unique book identifier, mandatory.
1185 * Use the URI, or ISBN if available.
1186 *
1187 * An unambiguous reference to the resource within a given context.
1188 *
1189 * Recommended best practice is to identify the resource by means of a
1190 * string conforming to a formal identification system.
1191 *
1192 * Used for the dc:identifier metadata parameter in the OPF file, as well
1193 * as dtb:uid in the NCX file.
1194 *
1195 * Identifier type should only be:
1196 * EPub::IDENTIFIER_URI
1197 * EPub::IDENTIFIER_ISBN
1198 * EPub::IDENTIFIER_UUID
1199 *
1200 * @param string $identifier
1201 * @param string $identifierType
1202 * @access public
1203 * @return bool $success
1204 */
1205 function setIdentifier($identifier, $identifierType) {
1206 if ($this->isFinalized || ($identifierType !== EPub::IDENTIFIER_URI && $identifierType !== EPub::IDENTIFIER_ISBN && $identifierType !== EPub::IDENTIFIER_UUID)) {
1207 return FALSE;
1208 }
1209 $this->identifier = $identifier;
1210 $this->identifierType = $identifierType;
1211 return TRUE;
1212 }
1213
1214 /**
1215 * Get Book identifier.
1216 *
1217 * @access public
1218 * @return $identifier
1219 */
1220 function getIdentifier() {
1221 return $this->identifier;
1222 }
1223
1224 /**
1225 * Get Book identifierType.
1226 *
1227 * @access public
1228 * @return $identifierType
1229 */
1230 function getIdentifierType() {
1231 return $this->identifierType;
1232 }
1233
1234 /**
1235 * Book description, optional.
1236 *
1237 * An account of the resource.
1238 *
1239 * Description may include but is not limited to: an abstract, a table of
1240 * contents, a graphical representation, or a free-text account of the
1241 * resource.
1242 *
1243 * Used for the dc:source metadata parameter in the OPF file
1244 *
1245 * @param string $description
1246 * @access public
1247 * @return bool $success
1248 */
1249 function setDescription($description) {
1250 if ($this->isFinalized) {
1251 return FALSE;
1252 }
1253 $this->description = $description;
1254 return TRUE;
1255 }
1256
1257 /**
1258 * Get Book description.
1259 *
1260 * @access public
1261 * @return $description
1262 */
1263 function getDescription() {
1264 return $this->description;
1265 }
1266
1267 /**
1268 * Book author or creator, optional.
1269 * The $authorSortKey is basically how the name is to be sorted, usually
1270 * it's "Lastname, First names" where the $author is the straight
1271 * "Firstnames Lastname"
1272 *
1273 * An entity primarily responsible for making the resource.
1274 *
1275 * Examples of a Creator include a person, an organization, or a service.
1276 * Typically, the name of a Creator should be used to indicate the entity.
1277 *
1278 * Used for the dc:creator metadata parameter in the OPF file and the
1279 * docAuthor attribure in the NCX file.
1280 * The sort key is used for the opf:file-as attribute in dc:creator.
1281 *
1282 * @param string $author
1283 * @param string $authorSortKey
1284 * @access public
1285 * @return bool $success
1286 */
1287 function setAuthor($author, $authorSortKey) {
1288 if ($this->isFinalized) {
1289 return FALSE;
1290 }
1291 $this->author = $author;
1292 $this->authorSortKey = $authorSortKey;
1293 return TRUE;
1294 }
1295
1296 /**
1297 * Get Book author.
1298 *
1299 * @access public
1300 * @return $author
1301 */
1302 function getAuthor() {
1303 return $this->author;
1304 }
1305
1306 /**
1307 * Publisher Information, optional.
1308 *
1309 * An entity responsible for making the resource available.
1310 *
1311 * Examples of a Publisher include a person, an organization, or a service.
1312 * Typically, the name of a Publisher should be used to indicate the entity.
1313 *
1314 * Used for the dc:publisher and dc:relation metadata parameters in the OPF file.
1315 *
1316 * @param string $publisherName
1317 * @param string $publisherURL
1318 * @access public
1319 * @return bool $success
1320 */
1321 function setPublisher($publisherName, $publisherURL) {
1322 if ($this->isFinalized) {
1323 return FALSE;
1324 }
1325 $this->publisherName = $publisherName;
1326 $this->publisherURL = $publisherURL;
1327 return TRUE;
1328 }
1329
1330 /**
1331 * Get Book publisherName.
1332 *
1333 * @access public
1334 * @return $publisherName
1335 */
1336 function getPublisherName() {
1337 return $this->publisherName;
1338 }
1339
1340 /**
1341 * Get Book publisherURL.
1342 *
1343 * @access public
1344 * @return $publisherURL
1345 */
1346 function getPublisherURL() {
1347 return $this->publisherURL;
1348 }
1349
1350 /**
1351 * Release date, optional. If left blank, the time of the finalization will
1352 * be used.
1353 *
1354 * A point or period of time associated with an event in the lifecycle of
1355 * the resource.
1356 *
1357 * Date may be used to express temporal information at any level of
1358 * granularity. Recommended best practice is to use an encoding scheme,
1359 * such as the W3CDTF profile of ISO 8601 [W3CDTF].
1360 *
1361 * Used for the dc:date metadata parameter in the OPF file
1362 *
1363 * @param long $timestamp
1364 * @access public
1365 * @return bool $success
1366 */
1367 function setDate($timestamp) {
1368 if ($this->isFinalized) {
1369 return FALSE;
1370 }
1371 $this->date = $timestamp;
1372 $this->opf->date = $timestamp;
1373 return TRUE;
1374 }
1375
1376 /**
1377 * Get Book date.
1378 *
1379 * @access public
1380 * @return $date
1381 */
1382 function getDate() {
1383 return $this->date;
1384 }
1385
1386 /**
1387 * Book (copy)rights, optional.
1388 *
1389 * Information about rights held in and over the resource.
1390 *
1391 * Typically, rights information includes a statement about various
1392 * property rights associated with the resource, including intellectual
1393 * property rights.
1394 *
1395 * Used for the dc:rights metadata parameter in the OPF file
1396 *
1397 * @param string $rightsText
1398 * @access public
1399 * @return bool $success
1400 */
1401 function setRights($rightsText) {
1402 if ($this->isFinalized) {
1403 return FALSE;
1404 }
1405 $this->rights = $rightsText;
1406 return TRUE;
1407 }
1408
1409 /**
1410 * Get Book rights.
1411 *
1412 * @access public
1413 * @return $rights
1414 */
1415 function getRights() {
1416 return $this->rights;
1417 }
1418
1419 /**
1420 * Add book Subject.
1421 *
1422 * The topic of the resource.
1423 *
1424 * Typically, the subject will be represented using keywords, key phrases,
1425 * or classification codes. Recommended best practice is to use a
1426 * controlled vocabulary. To describe the spatial or temporal topic of the
1427 * resource, use the Coverage element.
1428 *
1429 * @param string $subject
1430 */
1431 function setSubject($subject) {
1432 if ($this->isFinalized) {
1433 return;
1434 }
1435 $this->opf->addDCMeta(DublinCore::SUBJECT, $this->decodeHtmlEntities($subject));
1436 }
1437
1438 /**
1439 * Book source URL, optional.
1440 *
1441 * A related resource from which the described resource is derived.
1442 *
1443 * The described resource may be derived from the related resource in whole
1444 * or in part. Recommended best practice is to identify the related
1445 * resource by means of a string conforming to a formal identification system.
1446 *
1447 * Used for the dc:source metadata parameter in the OPF file
1448 *
1449 * @param string $sourceURL
1450 * @access public
1451 * @return bool $success
1452 */
1453 function setSourceURL($sourceURL) {
1454 if ($this->isFinalized) {
1455 return FALSE;
1456 }
1457 $this->sourceURL = $sourceURL;
1458 return TRUE;
1459 }
1460
1461 /**
1462 * Get Book sourceURL.
1463 *
1464 * @access public
1465 * @return $sourceURL
1466 */
1467 function getSourceURL() {
1468 return $this->sourceURL;
1469 }
1470
1471 /**
1472 * Coverage, optional.
1473 *
1474 * The spatial or temporal topic of the resource, the spatial applicability
1475 * of the resource, or the jurisdiction under which the resource is relevant.
1476 *
1477 * Spatial topic and spatial applicability may be a named place or a location
1478 * specified by its geographic coordinates. Temporal topic may be a named
1479 * period, date, or date range. A jurisdiction may be a named administrative
1480 * entity or a geographic place to which the resource applies. Recommended
1481 * best practice is to use a controlled vocabulary such as the Thesaurus of
1482 * Geographic Names [TGN]. Where appropriate, named places or time periods
1483 * can be used in preference to numeric identifiers such as sets of
1484 * coordinates or date ranges.
1485 *
1486 * Used for the dc:coverage metadata parameter in the OPF file
1487 *
1488 * Same as ->addDublinCoreMetadata(DublinCore::COVERAGE, $coverage);
1489 *
1490 * @param string $coverage
1491 * @access public
1492 * @return bool $success
1493 */
1494 function setCoverage($coverage) {
1495 if ($this->isFinalized) {
1496 return FALSE;
1497 }
1498 $this->coverage = $coverage;
1499 return TRUE;
1500 }
1501
1502 /**
1503 * Get Book coverage.
1504 *
1505 * @access public
1506 * @return $coverage
1507 */
1508 function getCoverage() {
1509 return $this->coverage;
1510 }
1511
1512 /**
1513 * Set book Relation.
1514 *
1515 * A related resource.
1516 *
1517 * Recommended best practice is to identify the related resource by means
1518 * of a string conforming to a formal identification system.
1519 *
1520 * @param string $relation
1521 */
1522 function setRelation($relation) {
1523 if ($this->isFinalized) {
1524 return;
1525 }
1526 $this->relation = $relation;
1527 }
1528
1529 /**
1530 * Get the book relation.
1531 *
1532 * @return string The relation.
1533 */
1534 function getRelation() {
1535 return $this->relation;
1536 }
1537
1538 /**
1539 * Set book Generator.
1540 *
1541 * The generator is a meta tag added to the ncx file, it is not visible
1542 * from within the book, but is a kind of electronic watermark.
1543 *
1544 * @param string $generator
1545 */
1546 function setGenerator($generator) {
1547 if ($this->isFinalized) {
1548 return;
1549 }
1550 $this->generator = $generator;
1551 }
1552
1553 /**
1554 * Get the book relation.
1555 *
1556 * @return string The generator identity string.
1557 */
1558 function getGenerator() {
1559 return $this->generator;
1560 }
1561
1562 /**
1563 * Set ePub date formate to the short yyyy-mm-dd form, for compliance with
1564 * a bug in EpubCheck, prior to its version 1.1.
1565 *
1566 * The latest version of ePubCheck can be obtained here:
1567 * http://code.google.com/p/epubcheck/
1568 *
1569 * @access public
1570 * @return bool $success
1571 */
1572 function setShortDateFormat() {
1573 if ($this->isFinalized) {
1574 return FALSE;
1575 }
1576 $this->dateformat = $this->dateformatShort;
1577 return TRUE;
1578 }
1579
1580 /**
1581 * @Deprecated
1582 */
1583 function setIgnoreEmptyBuffer($ignoreEmptyBuffer = TRUE) {
1584 die ("Function was deprecated, functionality is no longer needed.");
1585 }
1586
1587 /**
1588 * Set the references title for the ePub 3 landmarks section
1589 *
1590 * @param string $referencesTitle
1591 * @param string $referencesId
1592 * @param string $referencesClass
1593 * @return bool
1594 */
1595 function setReferencesTitle($referencesTitle = "Guide", $referencesId = "", $referencesClass = "references") {
1596 if ($this->isFinalized) {
1597 return FALSE;
1598 }
1599 $this->ncx->referencesTitle = is_string($referencesTitle) ? trim($referencesTitle) : "Guide";
1600 $this->ncx->referencesId = is_string($referencesId) ? trim($referencesId) : "references";
1601 $this->ncx->referencesClass = is_string($referencesClass) ? trim($referencesClass) : "references";
1602 return TRUE;
1603 }
1604
1605 /**
1606 * Set the references title for the ePub 3 landmarks section
1607 *
1608 * @param bool $referencesTitle
1609 */
1610 function setisReferencesAddedToToc($isReferencesAddedToToc = TRUE) {
1611 if ($this->isFinalized) {
1612 return FALSE;
1613 }
1614 $this->isReferencesAddedToToc = $isReferencesAddedToToc === TRUE;
1615 return TRUE;
1616 }
1617
1618 /**
1619 * Get Book status.
1620 *
1621 * @access public
1622 * @return bool
1623 */
1624 function isFinalized() {
1625 return $this->isFinalized;
1626 }
1627
1628 /**
1629 * Build the Table of Contents. This is not strictly necessary, as most eReaders will build it from the navigation structure in the .ncx file.
1630 *
1631 * @param string $cssFileName Include a link to this css file in the TOC html.
1632 * @param string $tocCSSClass The TOC is a <div>, if you need special formatting, you can add a css class for that div. Default is "toc".
1633 * @param string $title Title of the Table of contents. Default is "Table of Contents". Use this for ie. languages other than English.
1634 * @param bool $addReferences include reference pages in the TOC, using the $referencesOrder array to determine the order of the pages in the TOC. Default is TRUE.
1635 * @param bool $addToIndex Add the TOC to the NCX index at the current leve/position. Default is FALSE
1636 * @param string $tocFileName Change teh default name of the TOC file. The default is "TOC.xhtml"
1637 */
1638 function buildTOC($cssFileName = NULL, $tocCSSClass = "toc", $title = "Table of Contents", $addReferences = TRUE, $addToIndex = FALSE, $tocFileName = "TOC.xhtml") {
1639 if ($this->isFinalized) {
1640 return FALSE;
1641 }
1642 $this->buildTOC = TRUE;
1643 $this->tocTitle = $title;
1644 $this->tocFileName = $this->normalizeFileName($tocFileName);
1645 if (!empty($cssFileName)) {
1646 $this->tocCSSFileName = $this->normalizeFileName($cssFileName);
1647 }
1648 $this->tocCSSClass = $tocCSSClass;
1649 $this->tocAddReferences = $addReferences;
1650
1651 $this->opf->addItemRef("ref_" . Reference::TABLE_OF_CONTENTS, FALSE);
1652 $this->opf->addReference(Reference::TABLE_OF_CONTENTS, $title, $this->tocFileName);
1653
1654 if ($addToIndex) {
1655 $navPoint = new NavPoint($this->decodeHtmlEntities($title), $this->tocFileName, "ref_" . Reference::TABLE_OF_CONTENTS);
1656 $this->ncx->addNavPoint($navPoint);
1657 } else {
1658 $this->ncx->referencesList[Reference::TABLE_OF_CONTENTS] = $this->tocFileName;
1659 $this->ncx->referencesName[Reference::TABLE_OF_CONTENTS] = $title;
1660 }
1661 }
1662
1663 private function finalizeTOC() {
1664 if (!$this->buildTOC) {
1665 return FALSE;
1666 }
1667
1668 if (empty($this->tocTitle)) {
1669 $this->tocTitle = "Table of Contents";
1670 }
1671
1672 $tocData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1673
1674 if ($this->isEPubVersion2()) {
1675 $tocData .= "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n"
1676 . " \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
1677 . "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1678 . "<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
1679 } else {
1680 $tocData .= "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n"
1681 . "<head>\n<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n";
1682 }
1683
1684 if (!empty($this->tocCssFileName)) {
1685 $tocData .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"" . $this->tocCssFileName . "\" />\n";
1686 }
1687
1688 $tocData .= "<title>" . $this->tocTitle . "</title>\n"
1689 . "</head>\n"
1690 . "<body>\n"
1691 . "<h3>" . $this->tocTitle . "</h3>\n<div";
1692
1693 if (!empty($this->tocCSSClass)) {
1694 $tocData .= " class=\"" . $this->tocCSSClass . "\"";
1695 }
1696 $tocData .= ">\n";
1697
1698 while (list($item, $descriptive) = each($this->referencesOrder)) {
1699 if ($item === "text") {
1700 while (list($chapterName, $navPoint) = each($this->ncx->chapterList)) {
1701 $fileName = $navPoint->getContentSrc();
1702 $level = $navPoint->getLevel() -2;
72a85715 1703 $tocData .= "\t<p>" . str_repeat(" &#160; &#160; &#160;", $level) . "<a href=\"" . $this->sanitizeFileName($fileName) . "\">" . $chapterName . "</a></p>\n";
87090d8a 1704 }
1705 } else if ($this->tocAddReferences === TRUE) {
1706 if (array_key_exists($item, $this->ncx->referencesList)) {
1707 $tocData .= "\t<p><a href=\"" . $this->ncx->referencesList[$item] . "\">" . $descriptive . "</a></p>\n";
1708 } else if ($item === "toc") {
1709 $tocData .= "\t<p><a href=\"TOC.xhtml\">" . $this->tocTitle . "</a></p>\n";
1710 } else if ($item === "cover" && $this->isCoverImageSet) {
1711 $tocData .= "\t<p><a href=\"CoverPage.xhtml\">" . $descriptive . "</a></p>\n";
1712 }
1713 }
1714 }
1715 $tocData .= "</div>\n</body>\n</html>\n";
1716
1717 $this->addReferencePage($this->tocTitle, $this->tocFileName, $tocData, Reference::TABLE_OF_CONTENTS);
1718
1719 }
1720
1721 /**
1722 * @return bool
1723 */
1724 function isEPubVersion2() {
1725 return $this->bookVersion === EPub::BOOK_VERSION_EPUB2;
1726 }
1727
1728 /**
1729 * @param string $cssFileName
1730 * @param string $title
1731 * @return string
1732 */
1733 function buildEPub3TOC($cssFileName = NULL, $title = "Table of Contents") {
1734 $this->ncx->referencesOrder = $this->referencesOrder;
1735 $this->ncx->setDocTitle($this->decodeHtmlEntities($this->title));
1736 return $this->ncx->finalizeEPub3($title, $cssFileName);
1737 }
1738
1739 /**
1740 * @param string $fileName
1741 * @param string $tocData
1742 * @return bool
1743 */
1744 function addEPub3TOC($fileName, $tocData) {
1745 if ($this->isEPubVersion2() || $this->isFinalized || array_key_exists($fileName, $this->fileList)) {
1746 return FALSE;
1747 }
1748 $fileName = Zip::getRelativePath($fileName);
1749 $fileName = preg_replace('#^[/\.]+#i', "", $fileName);
1750
1751 $this->zip->addFile($tocData, $this->bookRoot.$fileName);
1752
1753 $this->fileList[$fileName] = $fileName;
1754 $this->opf->addItem("toc", $fileName, "application/xhtml+xml", "nav");
1755 return TRUE;
1756 }
1757
1758 /**
1759 * Check for mandatory parameters and finalize the e-book.
1760 * Once finalized, the book is locked for further additions.
1761 *
1762 * @return bool $success
1763 */
1764 function finalize() {
1765 if ($this->isFinalized || $this->chapterCount == 0 || empty($this->title) || empty($this->language)) {
1766 return FALSE;
1767 }
1768
1769 if (empty($this->identifier) || empty($this->identifierType)) {
1770 $this->setIdentifier($this->createUUID(4), EPub::IDENTIFIER_UUID);
1771 }
1772
1773 if ($this->date == 0) {
1774 $this->date = time();
1775 }
1776
1777 if (empty($this->sourceURL)) {
1778 $this->sourceURL = $this->getCurrentPageURL();
1779 }
1780
1781 if (empty($this->publisherURL)) {
1782 $this->sourceURL = $this->getCurrentServerURL();
1783 }
1784
1785 // Generate OPF data:
1786 $this->opf->setIdent("BookId");
1787 $this->opf->initialize($this->title, $this->language, $this->identifier, $this->identifierType);
1788
1789 $DCdate = new DublinCore(DublinCore::DATE, gmdate($this->dateformat, $this->date));
1790 $DCdate->addOpfAttr("event", "publication");
1791 $this->opf->metadata->addDublinCore($DCdate);
1792
1793 if (!empty($this->description)) {
1794 $this->opf->addDCMeta(DublinCore::DESCRIPTION, $this->decodeHtmlEntities($this->description));
1795 }
1796
1797 if (!empty($this->publisherName)) {
1798 $this->opf->addDCMeta(DublinCore::PUBLISHER, $this->decodeHtmlEntities($this->publisherName));
1799 }
1800
1801 if (!empty($this->publisherURL)) {
1802 $this->opf->addDCMeta(DublinCore::RELATION, $this->decodeHtmlEntities($this->publisherURL));
1803 }
1804
1805 if (!empty($this->author)) {
1806 $author = $this->decodeHtmlEntities($this->author);
1807 $this->opf->addCreator($author, $this->decodeHtmlEntities($this->authorSortKey), MarcCode::AUTHOR);
1808 $this->ncx->setDocAuthor($author);
1809 }
1810
1811 if (!empty($this->rights)) {
1812 $this->opf->addDCMeta(DublinCore::RIGHTS, $this->decodeHtmlEntities($this->rights));
1813 }
1814
1815 if (!empty($this->coverage)) {
1816 $this->opf->addDCMeta(DublinCore::COVERAGE, $this->decodeHtmlEntities($this->coverage));
1817 }
1818
1819 if (!empty($this->sourceURL)) {
1820 $this->opf->addDCMeta(DublinCore::SOURCE, $this->sourceURL);
1821 }
1822
1823 if (!empty($this->relation)) {
1824 $this->opf->addDCMeta(DublinCore::RELATION, $this->decodeHtmlEntities($this->relation));
1825 }
1826
1827 if ($this->isCoverImageSet) {
1828 $this->opf->addMeta("cover", "coverImage");
1829 }
1830
1831 if (!empty($this->generator)) {
1832 $gen = $this->decodeHtmlEntities($this->generator);
1833 $this->opf->addMeta("generator", $gen);
1834 $this->ncx->addMetaEntry("dtb:generator", $gen);
1835 }
1836
1837 if ($this->EPubMark) {
1838 $this->opf->addMeta("generator", "EPub (Version " . self::VERSION . ") by A. Grandt, http://www.phpclasses.org/package/6115");
1839 }
1840
1841 reset($this->ncx->chapterList);
1842 list($firstChapterName, $firstChapterNavPoint) = each($this->ncx->chapterList);
1843 $firstChapterFileName = $firstChapterNavPoint->getContentSrc();
1844 $this->opf->addReference(Reference::TEXT, $this->decodeHtmlEntities($firstChapterName), $firstChapterFileName);
1845
1846 $this->ncx->setUid($this->identifier);
1847
1848 $this->ncx->setDocTitle($this->decodeHtmlEntities($this->title));
1849
1850 $this->ncx->referencesOrder = $this->referencesOrder;
1851 if ($this->isReferencesAddedToToc) {
1852 $this->ncx->finalizeReferences();
1853 }
1854
1855 $this->finalizeTOC();
1856
1857 if (!$this->isEPubVersion2()) {
1858 $this->addEPub3TOC("epub3toc.xhtml", $this->buildEPub3TOC());
1859 }
1860
1861 $opfFinal = $this->fixEncoding($this->opf->finalize());
1862 $ncxFinal = $this->fixEncoding($this->ncx->finalize());
1863
1864 if (mb_detect_encoding($opfFinal, 'UTF-8', true) === "UTF-8") {
1865 $this->zip->addFile($opfFinal, $this->bookRoot."book.opf");
1866 } else {
1867 $this->zip->addFile(mb_convert_encoding($opfFinal, "UTF-8"), $this->bookRoot."book.opf");
1868 }
1869
1870 if (mb_detect_encoding($ncxFinal, 'UTF-8', true) === "UTF-8") {
1871 $this->zip->addFile($ncxFinal, $this->bookRoot."book.ncx");
1872 } else {
1873 $this->zip->addFile(mb_convert_encoding($ncxFinal, "UTF-8"), $this->bookRoot."book.ncx");
1874 }
1875
1876 $this->opf = NULL;
1877 $this->ncx = NULL;
1878
1879 $this->isFinalized = TRUE;
1880 return TRUE;
1881 }
1882
1883 /**
1884 * Ensure the encoded string is a valid UTF-8 string.
1885 *
1886 * Note, that a mb_detect_encoding on the returned string will still return ASCII if the entire string is comprized of characters in the 1-127 range.
1887 *
1888 * @link: http://snippetdb.com/php/convert-string-to-utf-8-for-mysql
1889 * @param string $in_str
1890 * @return string converted string.
1891 */
1892 function fixEncoding($in_str) {
1893 if (mb_detect_encoding($in_str) == "UTF-8" && mb_check_encoding($in_str,"UTF-8")) {
1894 return $in_str;
1895 } else {
1896 return utf8_encode($in_str);
1897 }
1898 }
1899
1900 /**
1901 * Return the finalized book.
1902 *
1903 * @return string with the book in binary form.
1904 */
1905 function getBook() {
1906 if (!$this->isFinalized) {
1907 $this->finalize();
1908 }
1909
1910 return $this->zip->getZipData();
1911 }
1912
1913 /**
1914 * Remove disallowed characters from string to get a nearly safe filename
1915 *
1916 * @param string $fileName
1917 * @return mixed|string
1918 */
1919 function sanitizeFileName($fileName) {
1920 $fileName1 = str_replace($this->forbiddenCharacters, '', $fileName);
1921 $fileName2 = preg_replace('/[\s-]+/', '-', $fileName1);
1922 return trim($fileName2, '.-_');
1923
1924 }
1925
1926 /**
1927 * Cleanup the filepath, and remove leading . and / characters.
1928 *
1929 * Sometimes, when a path is generated from multiple fragments,
1930 * you can get something like "../data/html/../images/image.jpeg"
1931 * ePub files don't work well with that, this will normalize that
1932 * example path to "data/images/image.jpeg"
1933 *
1934 * @param string $fileName
1935 * @return string normalized filename
1936 */
1937 function normalizeFileName($fileName) {
1938 return preg_replace('#^[/\.]+#i', "", Zip::getRelativePath($fileName));
1939 }
1940
1941 /**
1942 * Save the ePub file to local disk.
1943 *
1944 * @param string $fileName
1945 * @param string $baseDir If empty baseDir is absolute to server path, if omitted it's relative to script path
1946 * @return The sent file name if successfull, FALSE if it failed.
1947 */
1948 function saveBook($fileName, $baseDir = '.') {
1949
1950 // Make fileName safe
1951 $fileName = $this->sanitizeFileName($fileName);
1952
1953 // Finalize book, if it's not done already
1954 if (!$this->isFinalized) {
1955 $this->finalize();
1956 }
1957
1958 if (stripos(strrev($fileName), "bupe.") !== 0) {
1959 $fileName .= ".epub";
1960 }
1961
1962 // Try to open file access
1963 $fh = fopen($baseDir.'/'.$fileName, "w");
1964
1965 if ($fh) {
1966 fputs($fh, $this->getBook());
1967 fclose($fh);
1968
1969 // if file is written return TRUE
1970 return $fileName;
1971 }
1972
1973 // return FALSE by default
1974 return FALSE;
1975 }
1976
1977 /**
1978 * Return the finalized book size.
1979 *
1980 * @return string
1981 */
1982 function getBookSize() {
1983 if (!$this->isFinalized) {
1984 $this->finalize();
1985 }
1986
1987 return $this->zip->getArchiveSize();
1988 }
1989
1990 /**
1991 * Send the book as a zip download
1992 *
1993 * Sending will fail if the output buffer is in use. You can override this limit by
1994 * calling setIgnoreEmptyBuffer(TRUE), though the function will still fail if that
1995 * buffer is not empty.
1996 *
1997 * @param string $fileName The name of the book without the .epub at the end.
1998 * @return The sent file name if successfull, FALSE if it failed.
1999 */
2000 function sendBook($fileName) {
2001 if (!$this->isFinalized) {
2002 $this->finalize();
2003 }
2004
2005 if (stripos(strrev($fileName), "bupe.") !== 0) {
2006 $fileName .= ".epub";
2007 }
2008
2009 if (TRUE === $this->zip->sendZip($fileName, "application/epub+zip")) {
2010 return $fileName;
2011 }
2012 return FALSE;
2013 }
2014
2015 /**
2016 * Generates an UUID.
2017 *
2018 * Default version (4) will generate a random UUID, version 3 will URL based UUID.
2019 *
2020 * Added for convinience
2021 *
2022 * @param int $bookVersion UUID version to retrieve, See lib.uuid.manual.html for details.
2023 * @param string $url
2024 * @return string The formatted uuid
2025 */
2026 function createUUID($bookVersion = 4, $url = NULL) {
2027 include_once("lib.uuid.php");
2028 return UUID::mint($bookVersion, $url, UUID::nsURL);
2029 }
2030
2031 /**
2032 * Get the url of the current page.
2033 * Example use: Default Source URL
2034 *
2035 * $return string Page URL.
2036 */
2037 function getCurrentPageURL() {
2038 $pageURL = $this->getCurrentServerURL() . filter_input(INPUT_SERVER, "REQUEST_URI");
2039 return $pageURL;
2040 }
2041
2042 /**
2043 * Get the url of the server.
2044 * Example use: Default Publisher URL
2045 *
2046 * $return string Server URL.
2047 */
2048 function getCurrentServerURL() {
2049 $serverURL = 'http';
2050 $https = filter_input(INPUT_SERVER, "HTTPS");
2051 $port = filter_input(INPUT_SERVER, "SERVER_PORT");
2052
2053 if ($https === "on") {
2054 $serverURL .= "s";
2055 }
2056 $serverURL .= "://" . filter_input(INPUT_SERVER, "SERVER_NAME");
2057 if ($port != "80") {
2058 $serverURL .= ":" . $port;
2059 }
2060 return $serverURL . '/';
2061 }
2062
2063 /**
2064 * Try to determine the mimetype of the file path.
2065 *
2066 * @param string $source Path
2067 * @return string mimetype, or FALSE.
2068 */
2069 function getMime($source) {
2070 return $this->mimetypes[pathinfo($source, PATHINFO_EXTENSION)];
2071 }
2072
2073 /**
2074 * Get an image from a file or url, return it resized if the image exceeds the $maxImageWidth or $maxImageHeight directives.
2075 *
2076 * The return value is an array.
2077 * ['width'] is the width of the image.
2078 * ['height'] is the height of the image.
2079 * ['mime'] is the mime type of the image. Resized images are always in jpeg format.
2080 * ['image'] is the image data.
2081 * ['ext'] is the extension of the image file.
2082 *
2083 * @param string $source path or url to file.
2084 * $return array
2085 */
2086 function getImage($source) {
2087 $width = -1;
2088 $height = -1;
2089 $mime = "application/octet-stream";
2090 $type = FALSE;
2091 $ext = "";
2092
2093
2094 $image = $this->getFileContents($source);
2095
2096 if ($image !== FALSE && strlen($image) > 0) {
2097 $imageFile = imagecreatefromstring($image);
2098 if ($imageFile !== false) {
2099 $width = ImageSX($imageFile);
2100 $height = ImageSY($imageFile);
2101 }
2102 if ($this->isExifInstalled) {
2103 @$type = exif_imagetype($source);
2104 $mime = image_type_to_mime_type($type);
2105 }
2106 if ($mime === "application/octet-stream") {
2107 $mime = $this->image_file_type_from_binary($image);
2108 }
2109 if ($mime === "application/octet-stream") {
2110 $mime = $this->getMimeTypeFromUrl($source);
2111 }
2112 } else {
2113 return FALSE;
2114 }
2115
2116 if ($width <= 0 || $height <= 0) {
2117 return FALSE;
2118 }
2119
2120 $ratio = 1;
2121
2122 if ($this->isGdInstalled) {
2123 if ($width > $this->maxImageWidth) {
2124 $ratio = $this->maxImageWidth/$width;
2125 }
2126 if ($height*$ratio > $this->maxImageHeight) {
2127 $ratio = $this->maxImageHeight/$height;
2128 }
2129
2130 if ($ratio < 1 || empty($mime) || ($this->isGifImagesEnabled !== FALSE && $mime == "image/gif")) {
2131 $image_o = imagecreatefromstring($image);
2132 $image_p = imagecreatetruecolor($width*$ratio, $height*$ratio);
2133
2134 if ($mime == "image/png") {
2135 imagealphablending($image_p, false);
2136 imagesavealpha($image_p, true);
2137 imagealphablending($image_o, true);
2138
2139 imagecopyresampled($image_p, $image_o, 0, 0, 0, 0, ($width*$ratio), ($height*$ratio), $width, $height);
2140 ob_start();
2141 imagepng($image_p, NULL, 9);
2142 $image = ob_get_contents();
2143 ob_end_clean();
2144
2145 $ext = "png";
2146 } else {
2147 imagecopyresampled($image_p, $image_o, 0, 0, 0, 0, ($width*$ratio), ($height*$ratio), $width, $height);
2148 ob_start();
2149 imagejpeg($image_p, NULL, 80);
2150 $image = ob_get_contents();
2151 ob_end_clean();
2152
2153 $mime = "image/jpeg";
2154 $ext = "jpg";
2155 }
2156 imagedestroy($image_o);
2157 imagedestroy($image_p);
2158 }
2159 }
2160
2161 if ($ext === "") {
2162 static $mimeToExt = array (
2163 'image/jpeg' => 'jpg',
2164 'image/gif' => 'gif',
2165 'image/png' => 'png'
2166 );
2167
2168 if (isset($mimeToExt[$mime])) {
2169 $ext = $mimeToExt[$mime];
2170 }
2171 }
2172
2173 $rv = array();
2174 $rv['width'] = $width*$ratio;
2175 $rv['height'] = $height*$ratio;
2176 $rv['mime'] = $mime;
2177 $rv['image'] = $image;
2178 $rv['ext'] = $ext;
2179
2180 return $rv;
2181 }
2182
2183 /**
2184 * Get file contents, using curl if available, else file_get_contents
2185 *
2186 * @param string $source
2187 * @return bool
2188 */
2189 function getFileContents($source, $toTempFile = FALSE) {
2190 $isExternal = preg_match('#^(http|ftp)s?://#i', $source) == 1;
2191
2192 if ($isExternal && $this->isCurlInstalled) {
2193 $ch = curl_init();
2194 $outFile = NULL;
2195 $fp = NULL;
2196 $res = FALSE;
2197 $info = array('http_code' => 500);
2198
2199 curl_setopt($ch, CURLOPT_HEADER, 0);
2200 curl_setopt($ch, CURLOPT_URL, str_replace(" ","%20",$source));
2201 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
2202 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2203 curl_setopt($ch, CURLOPT_BUFFERSIZE, 4096);
2204
2205 if ($toTempFile) {
2206 $outFile = tempnam(sys_get_temp_dir(), "EPub_v" . EPub::VERSION . "_");
2207 $fp = fopen($outFile, "w+b");
2208 curl_setopt($ch, CURLOPT_FILE, $fp);
2209
2210 $res = curl_exec($ch);
2211 $info = curl_getinfo($ch);
2212
2213 curl_close($ch);
2214 fclose($fp);
2215 } else {
2216 $res = curl_exec($ch);
2217 $info = curl_getinfo($ch);
2218
2219 curl_close($ch);
2220 }
2221
2222 if ($info['http_code'] == 200 && $res != false) {
2223 if ($toTempFile) {
2224 return $outFile;
2225 }
2226 return $res;
2227 }
2228 return FALSE;
2229 }
2230
2231 if ($this->isFileGetContentsInstalled && (!$isExternal || $this->isFileGetContentsExtInstalled)) {
2232 @$data = file_get_contents($source);
2233 return $data;
2234 }
2235 return FALSE;
2236 }
2237
2238 /**
2239 * get mime type from image data
2240 *
2241 * By fireweasel found on http://stackoverflow.com/questions/2207095/get-image-mimetype-from-resource-in-php-gd
2242 * @staticvar array $type
2243 * @param object $binary
2244 * @return string
2245 */
2246 function image_file_type_from_binary($binary) {
2247 $hits = 0;
2248 if (!preg_match(
2249 '/\A(?:(\xff\xd8\xff)|(GIF8[79]a)|(\x89PNG\x0d\x0a)|(BM)|(\x49\x49(?:\x2a\x00|\x00\x4a))|(FORM.{4}ILBM))/',
2250 $binary, $hits)) {
2251 return 'application/octet-stream';
2252 }
2253 static $type = array (
2254 1 => 'image/jpeg',
2255 2 => 'image/gif',
2256 3 => 'image/png',
2257 4 => 'image/x-windows-bmp',
2258 5 => 'image/tiff',
2259 6 => 'image/x-ilbm',
2260 );
2261 return $type[count($hits) - 1];
2262 }
2263
2264 /**
2265 * @param string $source URL Source
2266 * @return string MimeType
2267 */
2268 function getMimeTypeFromUrl($source) {
2269 $ext = FALSE;
2270
2271 $srev = strrev($source);
2272 $pos = strpos($srev, "?");
2273 if ($pos !== FALSE) {
2274 $srev = substr($srev, $pos+1);
2275 }
2276
2277 $pos = strpos($srev, ".");
2278 if ($pos !== FALSE) {
2279 $ext = strtolower(strrev(substr($srev, 0, $pos)));
2280 }
2281
2282 if ($ext !== FALSE) {
2283 return $this->getMimeTypeFromExtension($ext);
2284 }
2285 return "application/octet-stream";
2286 }
2287
2288 /**
2289 * @param string $ext Extension
2290 * @return string MimeType
2291 */
2292 function getMimeTypeFromExtension($ext) {
2293 switch ($ext) {
2294 case "jpg":
2295 case "jpe":
2296 case "jpeg":
2297 return 'image/jpeg';
2298 case "gif":
2299 return 'image/gif';
2300 case "png":
2301 return 'image/png';
2302 case "bmp":
2303 return 'image/x-windows-bmp';
2304 case "tif":
2305 case "tiff":
2306 case "cpt":
2307 return 'image/tiff';
2308 case "lbm":
2309 case "ilbm":
2310 return 'image/x-ilbm';
2311 default:
2312 return "application/octet-stream";
2313 }
2314 }
2315
2316 /**
2317 * Encode html code to use html entities, safeguarding it from potential character encoding peoblems
2318 * This function is a bit different from the vanilla htmlentities function in that it does not encode html tags.
2319 *
2320 * The regexp is taken from the PHP Manual discussion, it was written by user "busbyjon".
2321 * http://www.php.net/manual/en/function.htmlentities.php#90111
2322 *
2323 * @param string $string string to encode.
2324 */
2325 public function encodeHtml($string) {
2326 $string = strtr($string, $this->html_encoding_characters);
2327
2328 //return preg_replace("/&amp;(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,5};)/", "&\\1", $string);
2329 //return preg_replace("/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,5};)/", "&amp;", $string);
2330 return $string;
2331 }
2332
2333 /**
2334 * Helper function to create a DOM fragment with given markup.
2335 *
2336 * @author Adam Schmalhofer
2337 *
2338 * @param DOMDocument $dom
2339 * @param string $markup
2340 * @return DOMNode fragment in a node.
2341 */
2342 protected function createDomFragment($dom, $markup) {
2343 $node = $dom->createDocumentFragment();
2344 $node->appendXML($markup);
2345 return $node;
2346 }
2347
2348 /**
2349 * Retrieve an array of file names currently added to the book.
2350 * $key is the filename used in the book
2351 * $value is the original filename, will be the same as $key for most entries
2352 *
2353 * @return array file list
2354 */
2355 function getFileList() {
2356 return $this->fileList;
2357 }
2358
2359 /**
2360 * @deprecated Use Zip::getRelativePath($relPath) instead.
2361 */
2362 function relPath($relPath) {
2363 die ("Function was deprecated, use Zip::getRelativePath(\$relPath); instead");
2364 }
2365
2366 /**
2367 * Set default chapter target size.
2368 * Default is 250000 bytes, and minimum is 10240 bytes.
2369 *
2370 * @param int $size segment size in bytes
2371 * @return void
2372 */
2373 function setSplitSize($size) {
2374 $this->splitDefaultSize = (int)$size;
2375 if ($size < 10240) {
2376 $this->splitDefaultSize = 10240; // Making the file smaller than 10k is not a good idea.
2377 }
2378 }
2379
2380 /**
2381 * Get the chapter target size.
2382 *
2383 * @return $size
2384 */
2385 function getSplitSize() {
2386 return $this->splitDefaultSize;
2387 }
2388
2389 /**
2390 * Remove all non essential html tags and entities.
2391 *
2392 * @global type $htmlEntities
2393 * @param string $string
2394 * @return string with the stripped entities.
2395 */
2396 function decodeHtmlEntities($string) {
2397 global $htmlEntities;
2398
2399 $string = preg_replace('~\s*<br\s*/*\s*>\s*~i', "\n", $string);
2400 $string = preg_replace('~\s*</(p|div)\s*>\s*~i', "\n\n", $string);
2401 $string = preg_replace('~<[^>]*>~', '', $string);
2402
2403 $string = strtr($string, $htmlEntities);
2404
2405 $string = str_replace('&', '&amp;', $string);
2406 $string = str_replace('&amp;amp;', '&amp;', $string);
2407 $string = preg_replace('~&amp;(#x*[a-fA-F0-9]+;)~', '&\1', $string);
2408 $string = str_replace('<', '&lt;', $string);
2409 $string = str_replace('>', '&gt;', $string);
2410
2411 return $string;
2412 }
2413
2414 /**
2415 * Simply remove all HTML tags, brute force and no finesse.
2416 *
2417 * @param string $string html
2418 * @return string
2419 */
2420 function html2text($string) {
2421 return preg_replace('~<[^>]*>~', '', $string);
2422 }
2423
2424 /**
2425 * @return string
2426 */
2427 function getLog() {
2428 return $this->log->getLog();
2429 }
2430}