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