aboutsummaryrefslogtreecommitdiffhomepage
path: root/inc/3rdparty/libraries/PHPePub/EPub.php
diff options
context:
space:
mode:
authorNicolas LÅ“uillet <nicolas@loeuillet.org>2014-05-29 18:54:06 +0200
committerNicolas LÅ“uillet <nicolas@loeuillet.org>2014-05-29 18:54:06 +0200
commita9f5e572dde4f986a498d2fbe92a38a1b22f9595 (patch)
tree80b5bfc9836ae92cc4929a4d72ae0b2730e568bc /inc/3rdparty/libraries/PHPePub/EPub.php
parent96834a47b09985e1c82b82857fc108f20e8b8f2b (diff)
parent8038b38802769031e050c753fc0a388a2276629e (diff)
downloadwallabag-1.7.0.tar.gz
wallabag-1.7.0.tar.zst
wallabag-1.7.0.zip
Merge pull request #712 from wallabag/dev1.7.0
1.7, call me "Premium version"
Diffstat (limited to 'inc/3rdparty/libraries/PHPePub/EPub.php')
-rw-r--r--inc/3rdparty/libraries/PHPePub/EPub.php2432
1 files changed, 2432 insertions, 0 deletions
diff --git a/inc/3rdparty/libraries/PHPePub/EPub.php b/inc/3rdparty/libraries/PHPePub/EPub.php
new file mode 100644
index 00000000..f1f41bd5
--- /dev/null
+++ b/inc/3rdparty/libraries/PHPePub/EPub.php
@@ -0,0 +1,2432 @@
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);
346 $fileName = $this->sanitizeFileName($fileName);
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,$bookTitle) {
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" . $bookTitle . "\n"
629 . "\t\t<div>\n"
630 . "\t\t\t<img src=\"" . $imgPath . "\" alt=\"Cover image\" style=\"height: 100%\"/>\n"
631 . "\t\t</div>\n"
632 . "\t</body>\n"
633 . "</html>\n";
634 } else {
635 $coverPage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
636 . "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n"
637 . "<head>"
638 . "\t<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n"
639 . "\t\t<title>Cover Image</title>\n"
640 . "\t\t<link type=\"text/css\" rel=\"stylesheet\" href=\"Styles/CoverPage.css\" />\n"
641 . "\t</head>\n"
642 . "\t<body>\n"
643 . "\t\t<section epub:type=\"cover\">\n"
644 . "\t" . $bookTitle . "\n"
645 . "\t\t\t<img src=\"" . $imgPath . "\" alt=\"Cover image\" style=\"height: 30%\"/>\n"
646 . "\t\t</section>\n"
647 . "\t</body>\n"
648 . "</html>\n";
649 }
650 $coverPageCss = "@page, body, div, img {\n"
651 . "\tpadding: 0pt;\n"
652 . "\tmargin:0pt;\n"
653 . "}\n\nbody {\n"
654 . "\ttext-align: center;\n"
655 . "}\n";
656
657 $this->addCSSFile("Styles/CoverPage.css", "CoverPageCss", $coverPageCss);
658 $this->addFile($imgPath, "CoverImage", $imageData, $mimetype);
659 $this->addReferencePage("CoverPage", "CoverPage.xhtml", $coverPage, "cover");
660 $this->isCoverImageSet = TRUE;
661 return TRUE;
662 }
663
664 /**
665 * Process external references from a HTML to the book. The chapter itself is not stored.
666 * the HTML is scanned for &lt;link..., &lt;style..., and &lt;img tags.
667 * Embedded CSS styles and links will also be processed.
668 * Script tags are not processed, as scripting should be avoided in e-books.
669 *
670 * EPub keeps track of added files, and duplicate files referenced across multiple
671 * chapters, are only added once.
672 *
673 * If the $doc is a string, it is assumed to be the content of an HTML file,
674 * else is it assumes to be a DOMDocument.
675 *
676 * Basedir is the root dir the HTML is supposed to "live" in, used to resolve
677 * relative references such as <code>&lt;img src="../images/image.png"/&gt;</code>
678 *
679 * $externalReferences determines how the function will handle external references.
680 *
681 * @param mixed &$doc (referenced)
682 * @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.
683 * @param string $baseDir Default is "", meaning it is pointing to the document root.
684 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
685 *
686 * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
687 */
688 protected function processChapterExternalReferences(&$doc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "") {
689 if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
690 return FALSE;
691 }
692
693 $backPath = preg_replace('#[^/]+/#i', "../", $htmlDir);
694 $isDocAString = is_string($doc);
695 $xmlDoc = NULL;
696
697 if ($isDocAString) {
698 $xmlDoc = new DOMDocument();
699 @$xmlDoc->loadHTML($doc);
700 } else {
701 $xmlDoc = $doc;
702 }
703
704 $this->processChapterStyles($xmlDoc, $externalReferences, $baseDir, $htmlDir);
705 $this->processChapterLinks($xmlDoc, $externalReferences, $baseDir, $htmlDir, $backPath);
706 $this->processChapterImages($xmlDoc, $externalReferences, $baseDir, $htmlDir, $backPath);
707 $this->processChapterSources($xmlDoc, $externalReferences, $baseDir, $htmlDir, $backPath);
708
709 if ($isDocAString) {
710 //$html = $xmlDoc->saveXML();
711
712 $htmlNode = $xmlDoc->getElementsByTagName("html");
713 $headNode = $xmlDoc->getElementsByTagName("head");
714 $bodyNode = $xmlDoc->getElementsByTagName("body");
715
716 $htmlNS = "";
717 for ($index = 0; $index < $htmlNode->item(0)->attributes->length; $index++) {
718 $nodeName = $htmlNode->item(0)->attributes->item($index)->nodeName;
719 $nodeValue = $htmlNode->item(0)->attributes->item($index)->nodeValue;
720
721 if ($nodeName != "xmlns") {
722 $htmlNS .= " $nodeName=\"$nodeValue\"";
723 }
724 }
725
726 $xml = new DOMDocument('1.0', "utf-8");
727 $xml->lookupPrefix("http://www.w3.org/1999/xhtml");
728 $xml->preserveWhiteSpace = FALSE;
729 $xml->formatOutput = TRUE;
730
731 $xml2Doc = new DOMDocument('1.0', "utf-8");
732 $xml2Doc->lookupPrefix("http://www.w3.org/1999/xhtml");
733 $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");
734 $html = $xml2Doc->getElementsByTagName("html")->item(0);
735 $html->appendChild($xml2Doc->importNode($headNode->item(0), TRUE));
736 $html->appendChild($xml2Doc->importNode($bodyNode->item(0), TRUE));
737
738 // force pretty printing and correct formatting, should not be needed, but it is.
739 $xml->loadXML($xml2Doc->saveXML());
740 $doc = $xml->saveXML();
741
742 if (!$this->isEPubVersion2()) {
743 $doc = preg_replace('#^\s*<!DOCTYPE\ .+?>\s*#im', '', $doc);
744 }
745 }
746 return TRUE;
747 }
748
749 /**
750 * Process images referenced from an CSS file to the book.
751 *
752 * $externalReferences determins how the function will handle external references.
753 *
754 * @param string &$cssFile (referenced)
755 * @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.
756 * @param string $baseDir Default is "", meaning it is pointing to the document root.
757 * @param string $cssDir The of the CSS file's directory from the root of the archive.
758 *
759 * @return bool FALSE if unsuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
760 */
761 protected function processCSSExternalReferences(&$cssFile, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $cssDir = "") {
762 if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
763 return FALSE;
764 }
765
766 $backPath = preg_replace('#[^/]+/#i', "../", $cssDir);
767 $imgs = null;
768 preg_match_all('#url\s*\([\'\"\s]*(.+?)[\'\"\s]*\)#im', $cssFile, $imgs, PREG_SET_ORDER);
769
770 $itemCount = count($imgs);
771 for ($idx = 0; $idx < $itemCount; $idx++) {
772 $img = $imgs[$idx];
773 if ($externalReferences === EPub::EXTERNAL_REF_REMOVE_IMAGES || $externalReferences === EPub::EXTERNAL_REF_REPLACE_IMAGES) {
774 $cssFile = str_replace($img[0], "", $cssFile);
775 } else {
776 $source = $img[1];
777
778 $pathData = pathinfo($source);
779 $internalSrc = $pathData['basename'];
780 $internalPath = "";
781 $isSourceExternal = FALSE;
782
783 if ($this->resolveImage($source, $internalPath, $internalSrc, $isSourceExternal, $baseDir, $cssDir, $backPath)) {
784 $cssFile = str_replace($img[0], "url('" . $backPath . $internalPath . "')", $cssFile);
785 } else if ($isSourceExternal) {
786 $cssFile = str_replace($img[0], "", $cssFile); // External image is missing
787 } // else do nothing, if the image is local, and missing, assume it's been generated.
788 }
789 }
790 return TRUE;
791 }
792
793 /**
794 * Process style tags in a DOMDocument. Styles will be passed as CSS files and reinserted into the document.
795 *
796 * @param DOMDocument &$xmlDoc (referenced)
797 * @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.
798 * @param string $baseDir Default is "", meaning it is pointing to the document root.
799 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
800 *
801 * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
802 */
803 protected function processChapterStyles(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "") {
804 if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
805 return FALSE;
806 }
807 // process inlined CSS styles in style tags.
808 $styles = $xmlDoc->getElementsByTagName("style");
809 $styleCount = $styles->length;
810 for ($styleIdx = 0; $styleIdx < $styleCount; $styleIdx++) {
811 $style = $styles->item($styleIdx);
812
813 $styleData = preg_replace('#[/\*\s]*\<\!\[CDATA\[[\s\*/]*#im', "", $style->nodeValue);
814 $styleData = preg_replace('#[/\*\s]*\]\]\>[\s\*/]*#im', "", $styleData);
815
816 $this->processCSSExternalReferences($styleData, $externalReferences, $baseDir, $htmlDir);
817 $style->nodeValue = "\n" . trim($styleData) . "\n";
818 }
819 return TRUE;
820 }
821
822 /**
823 * 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.
824 * Link types text/css will be passed as CSS files.
825 *
826 * @param DOMDocument &$xmlDoc (referenced)
827 * @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.
828 * @param string $baseDir Default is "", meaning it is pointing to the document root.
829 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
830 * @param string $backPath The path to get back to the root of the archive from $htmlDir.
831 *
832 * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
833 */
834 protected function processChapterLinks(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "", $backPath = "") {
835 if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
836 return FALSE;
837 }
838 // process link tags.
839 $links = $xmlDoc->getElementsByTagName("link");
840 $linkCount = $links->length;
841 for ($linkIdx = 0; $linkIdx < $linkCount; $linkIdx++) {
842 $link = $links->item($linkIdx);
843 $source = $link->attributes->getNamedItem("href")->nodeValue;
844 $sourceData = NULL;
845
846 $pathData = pathinfo($source);
847 $internalSrc = $pathData['basename'];
848
849 if (preg_match('#^(http|ftp)s?://#i', $source) == 1) {
850 $urlinfo = parse_url($source);
851
852 if (strpos($urlinfo['path'], $baseDir."/") !== FALSE) {
853 $internalSrc = substr($urlinfo['path'], strpos($urlinfo['path'], $baseDir."/") + strlen($baseDir) + 1);
854 }
855
856 @$sourceData = getFileContents($source);
857 } else if (strpos($source, "/") === 0) {
858 @$sourceData = file_get_contents($this->docRoot . $source);
859 } else {
860 @$sourceData = file_get_contents($this->docRoot . $baseDir . "/" . $source);
861 }
862
863 if (!empty($sourceData)) {
864 if (!array_key_exists($internalSrc, $this->fileList)) {
865 $mime = $link->attributes->getNamedItem("type")->nodeValue;
866 if (empty($mime)) {
867 $mime = "text/plain";
868 }
869 if ($mime == "text/css") {
870 $this->processCSSExternalReferences($sourceData, $externalReferences, $baseDir, $htmlDir);
871 $this->addCSSFile($internalSrc, $internalSrc, $sourceData, EPub::EXTERNAL_REF_IGNORE, $baseDir);
872 $link->setAttribute("href", $backPath . $internalSrc);
873 } else {
874 $this->addFile($internalSrc, $internalSrc, $sourceData, $mime);
875 }
876 $this->fileList[$internalSrc] = $source;
877 } else {
878 $link->setAttribute("href", $backPath . $internalSrc);
879 }
880 } // else do nothing, if the link is local, and missing, assume it's been generated.
881 }
882 return TRUE;
883 }
884
885 /**
886 * Process img tags in a DOMDocument.
887 * $externalReferences will determine what will happen to these images, and the img src will be rewritten accordingly.
888 *
889 * @param DOMDocument &$xmlDoc (referenced)
890 * @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.
891 * @param string $baseDir Default is "", meaning it is pointing to the document root.
892 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
893 * @param string $backPath The path to get back to the root of the archive from $htmlDir.
894 *
895 * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
896 */
897 protected function processChapterImages(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "", $backPath = "") {
898 if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
899 return FALSE;
900 }
901 // process img tags.
902 $postProcDomElememts = array();
903 $images = $xmlDoc->getElementsByTagName("img");
904 $itemCount = $images->length;
905
906 for ($idx = 0; $idx < $itemCount; $idx++) {
907 $img = $images->item($idx);
908
909 if ($externalReferences === EPub::EXTERNAL_REF_REMOVE_IMAGES) {
910 $postProcDomElememts[] = $img;
911 } else if ($externalReferences === EPub::EXTERNAL_REF_REPLACE_IMAGES) {
912 $altNode = $img->attributes->getNamedItem("alt");
913 $alt = "image";
914 if ($altNode !== NULL && strlen($altNode->nodeValue) > 0) {
915 $alt = $altNode->nodeValue;
916 }
917 $postProcDomElememts[] = array($img, $this->createDomFragment($xmlDoc, "<em>[" . $alt . "]</em>"));
918 } else {
919 $source = $img->attributes->getNamedItem("src")->nodeValue;
920
921 $parsedSource = parse_url($source);
922 $internalSrc = $this->sanitizeFileName(urldecode(pathinfo($parsedSource['path'], PATHINFO_BASENAME)));
923 $internalPath = "";
924 $isSourceExternal = FALSE;
925
926 if ($this->resolveImage($source, $internalPath, $internalSrc, $isSourceExternal, $baseDir, $htmlDir, $backPath)) {
927 $img->setAttribute("src", $backPath . $internalPath);
928 } else if ($isSourceExternal) {
929 $postProcDomElememts[] = $img; // External image is missing
930 } // else do nothing, if the image is local, and missing, assume it's been generated.
931 }
932 }
933
934 foreach ($postProcDomElememts as $target) {
935 if (is_array($target)) {
936 $target[0]->parentNode->replaceChild($target[1], $target[0]);
937 } else {
938 $target->parentNode->removeChild($target);
939 }
940 }
941 return TRUE;
942 }
943
944 /**
945 * Process source tags in a DOMDocument.
946 * $externalReferences will determine what will happen to these images, and the img src will be rewritten accordingly.
947 *
948 * @param DOMDocument &$xmlDoc (referenced)
949 * @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.
950 * @param string $baseDir Default is "", meaning it is pointing to the document root.
951 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
952 * @param string $backPath The path to get back to the root of the archive from $htmlDir.
953 *
954 * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
955 */
956 protected function processChapterSources(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "", $backPath = "") {
957 if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
958 return FALSE;
959 }
960
961 if ($this->bookVersion !== EPub::BOOK_VERSION_EPUB3) {
962 // ePub 2 does not support multimedia formats, and they must be removed.
963 $externalReferences = EPub::EXTERNAL_REF_REMOVE_IMAGES;
964 }
965
966 $postProcDomElememts = array();
967 $images = $xmlDoc->getElementsByTagName("source");
968 $itemCount = $images->length;
969 for ($idx = 0; $idx < $itemCount; $idx++) {
970 $img = $images->item($idx);
971 if ($externalReferences === EPub::EXTERNAL_REF_REMOVE_IMAGES) {
972 $postProcDomElememts[] = $img;
973 } else if ($externalReferences === EPub::EXTERNAL_REF_REPLACE_IMAGES) {
974 $altNode = $img->attributes->getNamedItem("alt");
975 $alt = "image";
976 if ($altNode !== NULL && strlen($altNode->nodeValue) > 0) {
977 $alt = $altNode->nodeValue;
978 }
979 $postProcDomElememts[] = array($img, $this->createDomFragment($xmlDoc, "[" . $alt . "]"));
980 } else {
981 $source = $img->attributes->getNamedItem("src")->nodeValue;
982
983 $parsedSource = parse_url($source);
984 $internalSrc = $this->sanitizeFileName(urldecode(pathinfo($parsedSource['path'], PATHINFO_BASENAME)));
985 $internalPath = "";
986 $isSourceExternal = FALSE;
987
988 if ($this->resolveMedia($source, $internalPath, $internalSrc, $isSourceExternal, $baseDir, $htmlDir, $backPath)) {
989 $img->setAttribute("src", $backPath . $internalPath);
990 } else if ($isSourceExternal) {
991 $postProcDomElememts[] = $img; // External image is missing
992 } // else do nothing, if the image is local, and missing, assume it's been generated.
993 }
994 }
995 }
996
997 /**
998 * Resolve an image src and determine it's target location and add it to the book.
999 *
1000 * @param string $source Image Source link.
1001 * @param string &$internalPath (referenced) Return value, will be set to the target path and name in the book.
1002 * @param string &$internalSrc (referenced) Return value, will be set to the target name in the book.
1003 * @param string &$isSourceExternal (referenced) Return value, will be set to TRUE if the image originated from a full URL.
1004 * @param string $baseDir Default is "", meaning it is pointing to the document root.
1005 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
1006 * @param string $backPath The path to get back to the root of the archive from $htmlDir.
1007 */
1008 protected function resolveImage($source, &$internalPath, &$internalSrc, &$isSourceExternal, $baseDir = "", $htmlDir = "", $backPath = "") {
1009 if ($this->isFinalized) {
1010 return FALSE;
1011 }
1012 $imageData = NULL;
1013
1014 if (preg_match('#^(http|ftp)s?://#i', $source) == 1) {
1015 $urlinfo = parse_url($source);
1016 $urlPath = pathinfo($urlinfo['path']);
1017
1018 if (strpos($urlinfo['path'], $baseDir."/") !== FALSE) {
1019 $internalSrc = $this->sanitizeFileName(urldecode(substr($urlinfo['path'], strpos($urlinfo['path'], $baseDir."/") + strlen($baseDir) + 1)));
1020 }
1021 $internalPath = $urlinfo["scheme"] . "/" . $urlinfo["host"] . "/" . pathinfo($urlinfo["path"], PATHINFO_DIRNAME);
1022 $isSourceExternal = TRUE;
1023 $imageData = $this->getImage($source);
1024 } else if (strpos($source, "/") === 0) {
1025 $internalPath = pathinfo($source, PATHINFO_DIRNAME);
1026
1027 $path = $source;
1028 if (!file_exists($path)) {
1029 $path = $this->docRoot . $path;
1030 }
1031
1032 $imageData = $this->getImage($path);
1033 } else {
1034 $internalPath = $htmlDir . "/" . preg_replace('#^[/\.]+#', '', pathinfo($source, PATHINFO_DIRNAME));
1035
1036 $path = $baseDir . "/" . $source;
1037 if (!file_exists($path)) {
1038 $path = $this->docRoot . $path;
1039 }
1040
1041 $imageData = $this->getImage($path);
1042 }
1043 if ($imageData !== FALSE) {
1044 $iSrcInfo = pathinfo($internalSrc);
1045 if (!empty($imageData['ext']) && $imageData['ext'] != $iSrcInfo['extension']) {
1046 $internalSrc = $iSrcInfo['filename'] . "." . $imageData['ext'];
1047 }
1048 $internalPath = Zip::getRelativePath("images/" . $internalPath . "/" . $internalSrc);
1049 if (!array_key_exists($internalPath, $this->fileList)) {
1050 $this->addFile($internalPath, "i_" . $internalSrc, $imageData['image'], $imageData['mime']);
1051 $this->fileList[$internalPath] = $source;
1052 }
1053 return TRUE;
1054 }
1055 return FALSE;
1056 }
1057
1058 /**
1059 * Resolve a media src and determine it's target location and add it to the book.
1060 *
1061 * @param string $source Source link.
1062 * @param string $internalPath (referenced) Return value, will be set to the target path and name in the book.
1063 * @param string $internalSrc (referenced) Return value, will be set to the target name in the book.
1064 * @param string $isSourceExternal (referenced) Return value, will be set to TRUE if the image originated from a full URL.
1065 * @param string $baseDir Default is "", meaning it is pointing to the document root.
1066 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
1067 * @param string $backPath The path to get back to the root of the archive from $htmlDir.
1068 */
1069 protected function resolveMedia($source, &$internalPath, &$internalSrc, &$isSourceExternal, $baseDir = "", $htmlDir = "", $backPath = "") {
1070 if ($this->isFinalized) {
1071 return FALSE;
1072 }
1073 $mediaPath = NULL;
1074 $tmpFile;
1075
1076 if (preg_match('#^(http|ftp)s?://#i', $source) == 1) {
1077 $urlinfo = parse_url($source);
1078
1079 if (strpos($urlinfo['path'], $baseDir."/") !== FALSE) {
1080 $internalSrc = substr($urlinfo['path'], strpos($urlinfo['path'], $baseDir."/") + strlen($baseDir) + 1);
1081 }
1082 $internalPath = $urlinfo["scheme"] . "/" . $urlinfo["host"] . "/" . pathinfo($urlinfo["path"], PATHINFO_DIRNAME);
1083 $isSourceExternal = TRUE;
1084 $mediaPath = $this->getFileContents($source, true);
1085 $tmpFile = $mediaPath;
1086 } else if (strpos($source, "/") === 0) {
1087 $internalPath = pathinfo($source, PATHINFO_DIRNAME);
1088
1089 $mediaPath = $source;
1090 if (!file_exists($mediaPath)) {
1091 $mediaPath = $this->docRoot . $mediaPath;
1092 }
1093 } else {
1094 $internalPath = $htmlDir . "/" . preg_replace('#^[/\.]+#', '', pathinfo($source, PATHINFO_DIRNAME));
1095
1096 $mediaPath = $baseDir . "/" . $source;
1097 if (!file_exists($mediaPath)) {
1098 $mediaPath = $this->docRoot . $mediaPath;
1099 }
1100 }
1101
1102 if ($mediaPath !== FALSE) {
1103 $mime = $this->getMime($source);
1104 $internalPath = Zip::getRelativePath("media/" . $internalPath . "/" . $internalSrc);
1105
1106 if (!array_key_exists($internalPath, $this->fileList) &&
1107 $this->addLargeFile($internalPath, "m_" . $internalSrc, $mediaPath, $mime)) {
1108 $this->fileList[$internalPath] = $source;
1109 }
1110 if (isset($tmpFile)) {
1111 unlink($tmpFile);
1112 }
1113 return TRUE;
1114 }
1115 return FALSE;
1116 }
1117
1118 /**
1119 * Get Book Chapter count.
1120 *
1121 * @access public
1122 * @return number of chapters
1123 */
1124 function getChapterCount() {
1125 return $this->chapterCount;
1126 }
1127
1128 /**
1129 * Book title, mandatory.
1130 *
1131 * Used for the dc:title metadata parameter in the OPF file as well as the DocTitle attribute in the NCX file.
1132 *
1133 * @param string $title
1134 * @access public
1135 * @return bool $success
1136 */
1137 function setTitle($title) {
1138 if ($this->isFinalized) {
1139 return FALSE;
1140 }
1141 $this->title = $title;
1142 return TRUE;
1143 }
1144
1145 /**
1146 * Get Book title.
1147 *
1148 * @access public
1149 * @return $title
1150 */
1151 function getTitle() {
1152 return $this->title;
1153 }
1154
1155 /**
1156 * Book language, mandatory
1157 *
1158 * Use the RFC3066 Language codes, such as "en", "da", "fr" etc.
1159 * Defaults to "en".
1160 *
1161 * Used for the dc:language metadata parameter in the OPF file.
1162 *
1163 * @param string $language
1164 * @access public
1165 * @return bool $success
1166 */
1167 function setLanguage($language) {
1168 if ($this->isFinalized || mb_strlen($language) != 2) {
1169 return FALSE;
1170 }
1171 $this->language = $language;
1172 return TRUE;
1173 }
1174
1175 /**
1176 * Get Book language.
1177 *
1178 * @access public
1179 * @return $language
1180 */
1181 function getLanguage() {
1182 return $this->language;
1183 }
1184
1185 /**
1186 * Unique book identifier, mandatory.
1187 * Use the URI, or ISBN if available.
1188 *
1189 * An unambiguous reference to the resource within a given context.
1190 *
1191 * Recommended best practice is to identify the resource by means of a
1192 * string conforming to a formal identification system.
1193 *
1194 * Used for the dc:identifier metadata parameter in the OPF file, as well
1195 * as dtb:uid in the NCX file.
1196 *
1197 * Identifier type should only be:
1198 * EPub::IDENTIFIER_URI
1199 * EPub::IDENTIFIER_ISBN
1200 * EPub::IDENTIFIER_UUID
1201 *
1202 * @param string $identifier
1203 * @param string $identifierType
1204 * @access public
1205 * @return bool $success
1206 */
1207 function setIdentifier($identifier, $identifierType) {
1208 if ($this->isFinalized || ($identifierType !== EPub::IDENTIFIER_URI && $identifierType !== EPub::IDENTIFIER_ISBN && $identifierType !== EPub::IDENTIFIER_UUID)) {
1209 return FALSE;
1210 }
1211 $this->identifier = $identifier;
1212 $this->identifierType = $identifierType;
1213 return TRUE;
1214 }
1215
1216 /**
1217 * Get Book identifier.
1218 *
1219 * @access public
1220 * @return $identifier
1221 */
1222 function getIdentifier() {
1223 return $this->identifier;
1224 }
1225
1226 /**
1227 * Get Book identifierType.
1228 *
1229 * @access public
1230 * @return $identifierType
1231 */
1232 function getIdentifierType() {
1233 return $this->identifierType;
1234 }
1235
1236 /**
1237 * Book description, optional.
1238 *
1239 * An account of the resource.
1240 *
1241 * Description may include but is not limited to: an abstract, a table of
1242 * contents, a graphical representation, or a free-text account of the
1243 * resource.
1244 *
1245 * Used for the dc:source metadata parameter in the OPF file
1246 *
1247 * @param string $description
1248 * @access public
1249 * @return bool $success
1250 */
1251 function setDescription($description) {
1252 if ($this->isFinalized) {
1253 return FALSE;
1254 }
1255 $this->description = $description;
1256 return TRUE;
1257 }
1258
1259 /**
1260 * Get Book description.
1261 *
1262 * @access public
1263 * @return $description
1264 */
1265 function getDescription() {
1266 return $this->description;
1267 }
1268
1269 /**
1270 * Book author or creator, optional.
1271 * The $authorSortKey is basically how the name is to be sorted, usually
1272 * it's "Lastname, First names" where the $author is the straight
1273 * "Firstnames Lastname"
1274 *
1275 * An entity primarily responsible for making the resource.
1276 *
1277 * Examples of a Creator include a person, an organization, or a service.
1278 * Typically, the name of a Creator should be used to indicate the entity.
1279 *
1280 * Used for the dc:creator metadata parameter in the OPF file and the
1281 * docAuthor attribure in the NCX file.
1282 * The sort key is used for the opf:file-as attribute in dc:creator.
1283 *
1284 * @param string $author
1285 * @param string $authorSortKey
1286 * @access public
1287 * @return bool $success
1288 */
1289 function setAuthor($author, $authorSortKey) {
1290 if ($this->isFinalized) {
1291 return FALSE;
1292 }
1293 $this->author = $author;
1294 $this->authorSortKey = $authorSortKey;
1295 return TRUE;
1296 }
1297
1298 /**
1299 * Get Book author.
1300 *
1301 * @access public
1302 * @return $author
1303 */
1304 function getAuthor() {
1305 return $this->author;
1306 }
1307
1308 /**
1309 * Publisher Information, optional.
1310 *
1311 * An entity responsible for making the resource available.
1312 *
1313 * Examples of a Publisher include a person, an organization, or a service.
1314 * Typically, the name of a Publisher should be used to indicate the entity.
1315 *
1316 * Used for the dc:publisher and dc:relation metadata parameters in the OPF file.
1317 *
1318 * @param string $publisherName
1319 * @param string $publisherURL
1320 * @access public
1321 * @return bool $success
1322 */
1323 function setPublisher($publisherName, $publisherURL) {
1324 if ($this->isFinalized) {
1325 return FALSE;
1326 }
1327 $this->publisherName = $publisherName;
1328 $this->publisherURL = $publisherURL;
1329 return TRUE;
1330 }
1331
1332 /**
1333 * Get Book publisherName.
1334 *
1335 * @access public
1336 * @return $publisherName
1337 */
1338 function getPublisherName() {
1339 return $this->publisherName;
1340 }
1341
1342 /**
1343 * Get Book publisherURL.
1344 *
1345 * @access public
1346 * @return $publisherURL
1347 */
1348 function getPublisherURL() {
1349 return $this->publisherURL;
1350 }
1351
1352 /**
1353 * Release date, optional. If left blank, the time of the finalization will
1354 * be used.
1355 *
1356 * A point or period of time associated with an event in the lifecycle of
1357 * the resource.
1358 *
1359 * Date may be used to express temporal information at any level of
1360 * granularity. Recommended best practice is to use an encoding scheme,
1361 * such as the W3CDTF profile of ISO 8601 [W3CDTF].
1362 *
1363 * Used for the dc:date metadata parameter in the OPF file
1364 *
1365 * @param long $timestamp
1366 * @access public
1367 * @return bool $success
1368 */
1369 function setDate($timestamp) {
1370 if ($this->isFinalized) {
1371 return FALSE;
1372 }
1373 $this->date = $timestamp;
1374 $this->opf->date = $timestamp;
1375 return TRUE;
1376 }
1377
1378 /**
1379 * Get Book date.
1380 *
1381 * @access public
1382 * @return $date
1383 */
1384 function getDate() {
1385 return $this->date;
1386 }
1387
1388 /**
1389 * Book (copy)rights, optional.
1390 *
1391 * Information about rights held in and over the resource.
1392 *
1393 * Typically, rights information includes a statement about various
1394 * property rights associated with the resource, including intellectual
1395 * property rights.
1396 *
1397 * Used for the dc:rights metadata parameter in the OPF file
1398 *
1399 * @param string $rightsText
1400 * @access public
1401 * @return bool $success
1402 */
1403 function setRights($rightsText) {
1404 if ($this->isFinalized) {
1405 return FALSE;
1406 }
1407 $this->rights = $rightsText;
1408 return TRUE;
1409 }
1410
1411 /**
1412 * Get Book rights.
1413 *
1414 * @access public
1415 * @return $rights
1416 */
1417 function getRights() {
1418 return $this->rights;
1419 }
1420
1421 /**
1422 * Add book Subject.
1423 *
1424 * The topic of the resource.
1425 *
1426 * Typically, the subject will be represented using keywords, key phrases,
1427 * or classification codes. Recommended best practice is to use a
1428 * controlled vocabulary. To describe the spatial or temporal topic of the
1429 * resource, use the Coverage element.
1430 *
1431 * @param string $subject
1432 */
1433 function setSubject($subject) {
1434 if ($this->isFinalized) {
1435 return;
1436 }
1437 $this->opf->addDCMeta(DublinCore::SUBJECT, $this->decodeHtmlEntities($subject));
1438 }
1439
1440 /**
1441 * Book source URL, optional.
1442 *
1443 * A related resource from which the described resource is derived.
1444 *
1445 * The described resource may be derived from the related resource in whole
1446 * or in part. Recommended best practice is to identify the related
1447 * resource by means of a string conforming to a formal identification system.
1448 *
1449 * Used for the dc:source metadata parameter in the OPF file
1450 *
1451 * @param string $sourceURL
1452 * @access public
1453 * @return bool $success
1454 */
1455 function setSourceURL($sourceURL) {
1456 if ($this->isFinalized) {
1457 return FALSE;
1458 }
1459 $this->sourceURL = $sourceURL;
1460 return TRUE;
1461 }
1462
1463 /**
1464 * Get Book sourceURL.
1465 *
1466 * @access public
1467 * @return $sourceURL
1468 */
1469 function getSourceURL() {
1470 return $this->sourceURL;
1471 }
1472
1473 /**
1474 * Coverage, optional.
1475 *
1476 * The spatial or temporal topic of the resource, the spatial applicability
1477 * of the resource, or the jurisdiction under which the resource is relevant.
1478 *
1479 * Spatial topic and spatial applicability may be a named place or a location
1480 * specified by its geographic coordinates. Temporal topic may be a named
1481 * period, date, or date range. A jurisdiction may be a named administrative
1482 * entity or a geographic place to which the resource applies. Recommended
1483 * best practice is to use a controlled vocabulary such as the Thesaurus of
1484 * Geographic Names [TGN]. Where appropriate, named places or time periods
1485 * can be used in preference to numeric identifiers such as sets of
1486 * coordinates or date ranges.
1487 *
1488 * Used for the dc:coverage metadata parameter in the OPF file
1489 *
1490 * Same as ->addDublinCoreMetadata(DublinCore::COVERAGE, $coverage);
1491 *
1492 * @param string $coverage
1493 * @access public
1494 * @return bool $success
1495 */
1496 function setCoverage($coverage) {
1497 if ($this->isFinalized) {
1498 return FALSE;
1499 }
1500 $this->coverage = $coverage;
1501 return TRUE;
1502 }
1503
1504 /**
1505 * Get Book coverage.
1506 *
1507 * @access public
1508 * @return $coverage
1509 */
1510 function getCoverage() {
1511 return $this->coverage;
1512 }
1513
1514 /**
1515 * Set book Relation.
1516 *
1517 * A related resource.
1518 *
1519 * Recommended best practice is to identify the related resource by means
1520 * of a string conforming to a formal identification system.
1521 *
1522 * @param string $relation
1523 */
1524 function setRelation($relation) {
1525 if ($this->isFinalized) {
1526 return;
1527 }
1528 $this->relation = $relation;
1529 }
1530
1531 /**
1532 * Get the book relation.
1533 *
1534 * @return string The relation.
1535 */
1536 function getRelation() {
1537 return $this->relation;
1538 }
1539
1540 /**
1541 * Set book Generator.
1542 *
1543 * The generator is a meta tag added to the ncx file, it is not visible
1544 * from within the book, but is a kind of electronic watermark.
1545 *
1546 * @param string $generator
1547 */
1548 function setGenerator($generator) {
1549 if ($this->isFinalized) {
1550 return;
1551 }
1552 $this->generator = $generator;
1553 }
1554
1555 /**
1556 * Get the book relation.
1557 *
1558 * @return string The generator identity string.
1559 */
1560 function getGenerator() {
1561 return $this->generator;
1562 }
1563
1564 /**
1565 * Set ePub date formate to the short yyyy-mm-dd form, for compliance with
1566 * a bug in EpubCheck, prior to its version 1.1.
1567 *
1568 * The latest version of ePubCheck can be obtained here:
1569 * http://code.google.com/p/epubcheck/
1570 *
1571 * @access public
1572 * @return bool $success
1573 */
1574 function setShortDateFormat() {
1575 if ($this->isFinalized) {
1576 return FALSE;
1577 }
1578 $this->dateformat = $this->dateformatShort;
1579 return TRUE;
1580 }
1581
1582 /**
1583 * @Deprecated
1584 */
1585 function setIgnoreEmptyBuffer($ignoreEmptyBuffer = TRUE) {
1586 die ("Function was deprecated, functionality is no longer needed.");
1587 }
1588
1589 /**
1590 * Set the references title for the ePub 3 landmarks section
1591 *
1592 * @param string $referencesTitle
1593 * @param string $referencesId
1594 * @param string $referencesClass
1595 * @return bool
1596 */
1597 function setReferencesTitle($referencesTitle = "Guide", $referencesId = "", $referencesClass = "references") {
1598 if ($this->isFinalized) {
1599 return FALSE;
1600 }
1601 $this->ncx->referencesTitle = is_string($referencesTitle) ? trim($referencesTitle) : "Guide";
1602 $this->ncx->referencesId = is_string($referencesId) ? trim($referencesId) : "references";
1603 $this->ncx->referencesClass = is_string($referencesClass) ? trim($referencesClass) : "references";
1604 return TRUE;
1605 }
1606
1607 /**
1608 * Set the references title for the ePub 3 landmarks section
1609 *
1610 * @param bool $referencesTitle
1611 */
1612 function setisReferencesAddedToToc($isReferencesAddedToToc = TRUE) {
1613 if ($this->isFinalized) {
1614 return FALSE;
1615 }
1616 $this->isReferencesAddedToToc = $isReferencesAddedToToc === TRUE;
1617 return TRUE;
1618 }
1619
1620 /**
1621 * Get Book status.
1622 *
1623 * @access public
1624 * @return bool
1625 */
1626 function isFinalized() {
1627 return $this->isFinalized;
1628 }
1629
1630 /**
1631 * Build the Table of Contents. This is not strictly necessary, as most eReaders will build it from the navigation structure in the .ncx file.
1632 *
1633 * @param string $cssFileName Include a link to this css file in the TOC html.
1634 * @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".
1635 * @param string $title Title of the Table of contents. Default is "Table of Contents". Use this for ie. languages other than English.
1636 * @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.
1637 * @param bool $addToIndex Add the TOC to the NCX index at the current leve/position. Default is FALSE
1638 * @param string $tocFileName Change teh default name of the TOC file. The default is "TOC.xhtml"
1639 */
1640 function buildTOC($cssFileName = NULL, $tocCSSClass = "toc", $title = "Table of Contents", $addReferences = TRUE, $addToIndex = FALSE, $tocFileName = "TOC.xhtml") {
1641 if ($this->isFinalized) {
1642 return FALSE;
1643 }
1644 $this->buildTOC = TRUE;
1645 $this->tocTitle = $title;
1646 $this->tocFileName = $this->normalizeFileName($tocFileName);
1647 if (!empty($cssFileName)) {
1648 $this->tocCSSFileName = $this->normalizeFileName($cssFileName);
1649 }
1650 $this->tocCSSClass = $tocCSSClass;
1651 $this->tocAddReferences = $addReferences;
1652
1653 $this->opf->addItemRef("ref_" . Reference::TABLE_OF_CONTENTS, FALSE);
1654 $this->opf->addReference(Reference::TABLE_OF_CONTENTS, $title, $this->tocFileName);
1655
1656 if ($addToIndex) {
1657 $navPoint = new NavPoint($this->decodeHtmlEntities($title), $this->tocFileName, "ref_" . Reference::TABLE_OF_CONTENTS);
1658 $this->ncx->addNavPoint($navPoint);
1659 } else {
1660 $this->ncx->referencesList[Reference::TABLE_OF_CONTENTS] = $this->tocFileName;
1661 $this->ncx->referencesName[Reference::TABLE_OF_CONTENTS] = $title;
1662 }
1663 }
1664
1665 private function finalizeTOC() {
1666 if (!$this->buildTOC) {
1667 return FALSE;
1668 }
1669
1670 if (empty($this->tocTitle)) {
1671 $this->tocTitle = "Table of Contents";
1672 }
1673
1674 $tocData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1675
1676 if ($this->isEPubVersion2()) {
1677 $tocData .= "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n"
1678 . " \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
1679 . "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1680 . "<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
1681 } else {
1682 $tocData .= "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n"
1683 . "<head>\n<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n";
1684 }
1685
1686 if (!empty($this->tocCssFileName)) {
1687 $tocData .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"" . $this->tocCssFileName . "\" />\n";
1688 }
1689
1690 $tocData .= "<title>" . $this->tocTitle . "</title>\n"
1691 . "</head>\n"
1692 . "<body>\n"
1693 . "<h3>" . $this->tocTitle . "</h3>\n<div";
1694
1695 if (!empty($this->tocCSSClass)) {
1696 $tocData .= " class=\"" . $this->tocCSSClass . "\"";
1697 }
1698 $tocData .= ">\n";
1699
1700 while (list($item, $descriptive) = each($this->referencesOrder)) {
1701 if ($item === "text") {
1702 while (list($chapterName, $navPoint) = each($this->ncx->chapterList)) {
1703 $fileName = $navPoint->getContentSrc();
1704 $level = $navPoint->getLevel() -2;
1705 $tocData .= "\t<p>" . str_repeat(" &#160; &#160; &#160;", $level) . "<a href=\"" . $this->sanitizeFileName($fileName) . "\">" . $chapterName . "</a></p>\n";
1706 }
1707 } else if ($this->tocAddReferences === TRUE) {
1708 if (array_key_exists($item, $this->ncx->referencesList)) {
1709 $tocData .= "\t<p><a href=\"" . $this->ncx->referencesList[$item] . "\">" . $descriptive . "</a></p>\n";
1710 } else if ($item === "toc") {
1711 $tocData .= "\t<p><a href=\"TOC.xhtml\">" . $this->tocTitle . "</a></p>\n";
1712 } else if ($item === "cover" && $this->isCoverImageSet) {
1713 $tocData .= "\t<p><a href=\"CoverPage.xhtml\">" . $descriptive . "</a></p>\n";
1714 }
1715 }
1716 }
1717 $tocData .= "</div>\n</body>\n</html>\n";
1718
1719 $this->addReferencePage($this->tocTitle, $this->tocFileName, $tocData, Reference::TABLE_OF_CONTENTS);
1720
1721 }
1722
1723 /**
1724 * @return bool
1725 */
1726 function isEPubVersion2() {
1727 return $this->bookVersion === EPub::BOOK_VERSION_EPUB2;
1728 }
1729
1730 /**
1731 * @param string $cssFileName
1732 * @param string $title
1733 * @return string
1734 */
1735 function buildEPub3TOC($cssFileName = NULL, $title = "Table of Contents") {
1736 $this->ncx->referencesOrder = $this->referencesOrder;
1737 $this->ncx->setDocTitle($this->decodeHtmlEntities($this->title));
1738 return $this->ncx->finalizeEPub3($title, $cssFileName);
1739 }
1740
1741 /**
1742 * @param string $fileName
1743 * @param string $tocData
1744 * @return bool
1745 */
1746 function addEPub3TOC($fileName, $tocData) {
1747 if ($this->isEPubVersion2() || $this->isFinalized || array_key_exists($fileName, $this->fileList)) {
1748 return FALSE;
1749 }
1750 $fileName = Zip::getRelativePath($fileName);
1751 $fileName = preg_replace('#^[/\.]+#i', "", $fileName);
1752
1753 $this->zip->addFile($tocData, $this->bookRoot.$fileName);
1754
1755 $this->fileList[$fileName] = $fileName;
1756 $this->opf->addItem("toc", $fileName, "application/xhtml+xml", "nav");
1757 return TRUE;
1758 }
1759
1760 /**
1761 * Check for mandatory parameters and finalize the e-book.
1762 * Once finalized, the book is locked for further additions.
1763 *
1764 * @return bool $success
1765 */
1766 function finalize() {
1767 if ($this->isFinalized || $this->chapterCount == 0 || empty($this->title) || empty($this->language)) {
1768 return FALSE;
1769 }
1770
1771 if (empty($this->identifier) || empty($this->identifierType)) {
1772 $this->setIdentifier($this->createUUID(4), EPub::IDENTIFIER_UUID);
1773 }
1774
1775 if ($this->date == 0) {
1776 $this->date = time();
1777 }
1778
1779 if (empty($this->sourceURL)) {
1780 $this->sourceURL = $this->getCurrentPageURL();
1781 }
1782
1783 if (empty($this->publisherURL)) {
1784 $this->sourceURL = $this->getCurrentServerURL();
1785 }
1786
1787 // Generate OPF data:
1788 $this->opf->setIdent("BookId");
1789 $this->opf->initialize($this->title, $this->language, $this->identifier, $this->identifierType);
1790
1791 $DCdate = new DublinCore(DublinCore::DATE, gmdate($this->dateformat, $this->date));
1792 $DCdate->addOpfAttr("event", "publication");
1793 $this->opf->metadata->addDublinCore($DCdate);
1794
1795 if (!empty($this->description)) {
1796 $this->opf->addDCMeta(DublinCore::DESCRIPTION, $this->decodeHtmlEntities($this->description));
1797 }
1798
1799 if (!empty($this->publisherName)) {
1800 $this->opf->addDCMeta(DublinCore::PUBLISHER, $this->decodeHtmlEntities($this->publisherName));
1801 }
1802
1803 if (!empty($this->publisherURL)) {
1804 $this->opf->addDCMeta(DublinCore::RELATION, $this->decodeHtmlEntities($this->publisherURL));
1805 }
1806
1807 if (!empty($this->author)) {
1808 $author = $this->decodeHtmlEntities($this->author);
1809 $this->opf->addCreator($author, $this->decodeHtmlEntities($this->authorSortKey), MarcCode::AUTHOR);
1810 $this->ncx->setDocAuthor($author);
1811 }
1812
1813 if (!empty($this->rights)) {
1814 $this->opf->addDCMeta(DublinCore::RIGHTS, $this->decodeHtmlEntities($this->rights));
1815 }
1816
1817 if (!empty($this->coverage)) {
1818 $this->opf->addDCMeta(DublinCore::COVERAGE, $this->decodeHtmlEntities($this->coverage));
1819 }
1820
1821 if (!empty($this->sourceURL)) {
1822 $this->opf->addDCMeta(DublinCore::SOURCE, $this->sourceURL);
1823 }
1824
1825 if (!empty($this->relation)) {
1826 $this->opf->addDCMeta(DublinCore::RELATION, $this->decodeHtmlEntities($this->relation));
1827 }
1828
1829 if ($this->isCoverImageSet) {
1830 $this->opf->addMeta("cover", "coverImage");
1831 }
1832
1833 if (!empty($this->generator)) {
1834 $gen = $this->decodeHtmlEntities($this->generator);
1835 $this->opf->addMeta("generator", $gen);
1836 $this->ncx->addMetaEntry("dtb:generator", $gen);
1837 }
1838
1839 if ($this->EPubMark) {
1840 $this->opf->addMeta("generator", "EPub (Version " . self::VERSION . ") by A. Grandt, http://www.phpclasses.org/package/6115");
1841 }
1842
1843 reset($this->ncx->chapterList);
1844 list($firstChapterName, $firstChapterNavPoint) = each($this->ncx->chapterList);
1845 $firstChapterFileName = $firstChapterNavPoint->getContentSrc();
1846 $this->opf->addReference(Reference::TEXT, $this->decodeHtmlEntities($firstChapterName), $firstChapterFileName);
1847
1848 $this->ncx->setUid($this->identifier);
1849
1850 $this->ncx->setDocTitle($this->decodeHtmlEntities($this->title));
1851
1852 $this->ncx->referencesOrder = $this->referencesOrder;
1853 if ($this->isReferencesAddedToToc) {
1854 $this->ncx->finalizeReferences();
1855 }
1856
1857 $this->finalizeTOC();
1858
1859 if (!$this->isEPubVersion2()) {
1860 $this->addEPub3TOC("epub3toc.xhtml", $this->buildEPub3TOC());
1861 }
1862
1863 $opfFinal = $this->fixEncoding($this->opf->finalize());
1864 $ncxFinal = $this->fixEncoding($this->ncx->finalize());
1865
1866 if (mb_detect_encoding($opfFinal, 'UTF-8', true) === "UTF-8") {
1867 $this->zip->addFile($opfFinal, $this->bookRoot."book.opf");
1868 } else {
1869 $this->zip->addFile(mb_convert_encoding($opfFinal, "UTF-8"), $this->bookRoot."book.opf");
1870 }
1871
1872 if (mb_detect_encoding($ncxFinal, 'UTF-8', true) === "UTF-8") {
1873 $this->zip->addFile($ncxFinal, $this->bookRoot."book.ncx");
1874 } else {
1875 $this->zip->addFile(mb_convert_encoding($ncxFinal, "UTF-8"), $this->bookRoot."book.ncx");
1876 }
1877
1878 $this->opf = NULL;
1879 $this->ncx = NULL;
1880
1881 $this->isFinalized = TRUE;
1882 return TRUE;
1883 }
1884
1885 /**
1886 * Ensure the encoded string is a valid UTF-8 string.
1887 *
1888 * 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.
1889 *
1890 * @link: http://snippetdb.com/php/convert-string-to-utf-8-for-mysql
1891 * @param string $in_str
1892 * @return string converted string.
1893 */
1894 function fixEncoding($in_str) {
1895 if (mb_detect_encoding($in_str) == "UTF-8" && mb_check_encoding($in_str,"UTF-8")) {
1896 return $in_str;
1897 } else {
1898 return utf8_encode($in_str);
1899 }
1900 }
1901
1902 /**
1903 * Return the finalized book.
1904 *
1905 * @return string with the book in binary form.
1906 */
1907 function getBook() {
1908 if (!$this->isFinalized) {
1909 $this->finalize();
1910 }
1911
1912 return $this->zip->getZipData();
1913 }
1914
1915 /**
1916 * Remove disallowed characters from string to get a nearly safe filename
1917 *
1918 * @param string $fileName
1919 * @return mixed|string
1920 */
1921 function sanitizeFileName($fileName) {
1922 $fileName1 = str_replace($this->forbiddenCharacters, '', $fileName);
1923 $fileName2 = preg_replace('/[\s-]+/', '-', $fileName1);
1924 return trim($fileName2, '.-_');
1925
1926 }
1927
1928 /**
1929 * Cleanup the filepath, and remove leading . and / characters.
1930 *
1931 * Sometimes, when a path is generated from multiple fragments,
1932 * you can get something like "../data/html/../images/image.jpeg"
1933 * ePub files don't work well with that, this will normalize that
1934 * example path to "data/images/image.jpeg"
1935 *
1936 * @param string $fileName
1937 * @return string normalized filename
1938 */
1939 function normalizeFileName($fileName) {
1940 return preg_replace('#^[/\.]+#i', "", Zip::getRelativePath($fileName));
1941 }
1942
1943 /**
1944 * Save the ePub file to local disk.
1945 *
1946 * @param string $fileName
1947 * @param string $baseDir If empty baseDir is absolute to server path, if omitted it's relative to script path
1948 * @return The sent file name if successfull, FALSE if it failed.
1949 */
1950 function saveBook($fileName, $baseDir = '.') {
1951
1952 // Make fileName safe
1953 $fileName = $this->sanitizeFileName($fileName);
1954
1955 // Finalize book, if it's not done already
1956 if (!$this->isFinalized) {
1957 $this->finalize();
1958 }
1959
1960 if (stripos(strrev($fileName), "bupe.") !== 0) {
1961 $fileName .= ".epub";
1962 }
1963
1964 // Try to open file access
1965 $fh = fopen($baseDir.'/'.$fileName, "w");
1966
1967 if ($fh) {
1968 fputs($fh, $this->getBook());
1969 fclose($fh);
1970
1971 // if file is written return TRUE
1972 return $fileName;
1973 }
1974
1975 // return FALSE by default
1976 return FALSE;
1977 }
1978
1979 /**
1980 * Return the finalized book size.
1981 *
1982 * @return string
1983 */
1984 function getBookSize() {
1985 if (!$this->isFinalized) {
1986 $this->finalize();
1987 }
1988
1989 return $this->zip->getArchiveSize();
1990 }
1991
1992 /**
1993 * Send the book as a zip download
1994 *
1995 * Sending will fail if the output buffer is in use. You can override this limit by
1996 * calling setIgnoreEmptyBuffer(TRUE), though the function will still fail if that
1997 * buffer is not empty.
1998 *
1999 * @param string $fileName The name of the book without the .epub at the end.
2000 * @return The sent file name if successfull, FALSE if it failed.
2001 */
2002 function sendBook($fileName) {
2003 if (!$this->isFinalized) {
2004 $this->finalize();
2005 }
2006
2007 if (stripos(strrev($fileName), "bupe.") !== 0) {
2008 $fileName .= ".epub";
2009 }
2010
2011 if (TRUE === $this->zip->sendZip($fileName, "application/epub+zip")) {
2012 return $fileName;
2013 }
2014 return FALSE;
2015 }
2016
2017 /**
2018 * Generates an UUID.
2019 *
2020 * Default version (4) will generate a random UUID, version 3 will URL based UUID.
2021 *
2022 * Added for convinience
2023 *
2024 * @param int $bookVersion UUID version to retrieve, See lib.uuid.manual.html for details.
2025 * @param string $url
2026 * @return string The formatted uuid
2027 */
2028 function createUUID($bookVersion = 4, $url = NULL) {
2029 include_once("lib.uuid.php");
2030 return UUID::mint($bookVersion, $url, UUID::nsURL);
2031 }
2032
2033 /**
2034 * Get the url of the current page.
2035 * Example use: Default Source URL
2036 *
2037 * $return string Page URL.
2038 */
2039 function getCurrentPageURL() {
2040 $pageURL = $this->getCurrentServerURL() . filter_input(INPUT_SERVER, "REQUEST_URI");
2041 return $pageURL;
2042 }
2043
2044 /**
2045 * Get the url of the server.
2046 * Example use: Default Publisher URL
2047 *
2048 * $return string Server URL.
2049 */
2050 function getCurrentServerURL() {
2051 $serverURL = 'http';
2052 $https = filter_input(INPUT_SERVER, "HTTPS");
2053 $port = filter_input(INPUT_SERVER, "SERVER_PORT");
2054
2055 if ($https === "on") {
2056 $serverURL .= "s";
2057 }
2058 $serverURL .= "://" . filter_input(INPUT_SERVER, "SERVER_NAME");
2059 if ($port != "80") {
2060 $serverURL .= ":" . $port;
2061 }
2062 return $serverURL . '/';
2063 }
2064
2065 /**
2066 * Try to determine the mimetype of the file path.
2067 *
2068 * @param string $source Path
2069 * @return string mimetype, or FALSE.
2070 */
2071 function getMime($source) {
2072 return $this->mimetypes[pathinfo($source, PATHINFO_EXTENSION)];
2073 }
2074
2075 /**
2076 * Get an image from a file or url, return it resized if the image exceeds the $maxImageWidth or $maxImageHeight directives.
2077 *
2078 * The return value is an array.
2079 * ['width'] is the width of the image.
2080 * ['height'] is the height of the image.
2081 * ['mime'] is the mime type of the image. Resized images are always in jpeg format.
2082 * ['image'] is the image data.
2083 * ['ext'] is the extension of the image file.
2084 *
2085 * @param string $source path or url to file.
2086 * $return array
2087 */
2088 function getImage($source) {
2089 $width = -1;
2090 $height = -1;
2091 $mime = "application/octet-stream";
2092 $type = FALSE;
2093 $ext = "";
2094
2095
2096 $image = $this->getFileContents($source);
2097
2098 if ($image !== FALSE && strlen($image) > 0) {
2099 $imageFile = imagecreatefromstring($image);
2100 if ($imageFile !== false) {
2101 $width = ImageSX($imageFile);
2102 $height = ImageSY($imageFile);
2103 }
2104 if ($this->isExifInstalled) {
2105 @$type = exif_imagetype($source);
2106 $mime = image_type_to_mime_type($type);
2107 }
2108 if ($mime === "application/octet-stream") {
2109 $mime = $this->image_file_type_from_binary($image);
2110 }
2111 if ($mime === "application/octet-stream") {
2112 $mime = $this->getMimeTypeFromUrl($source);
2113 }
2114 } else {
2115 return FALSE;
2116 }
2117
2118 if ($width <= 0 || $height <= 0) {
2119 return FALSE;
2120 }
2121
2122 $ratio = 1;
2123
2124 if ($this->isGdInstalled) {
2125 if ($width > $this->maxImageWidth) {
2126 $ratio = $this->maxImageWidth/$width;
2127 }
2128 if ($height*$ratio > $this->maxImageHeight) {
2129 $ratio = $this->maxImageHeight/$height;
2130 }
2131
2132 if ($ratio < 1 || empty($mime) || ($this->isGifImagesEnabled !== FALSE && $mime == "image/gif")) {
2133 $image_o = imagecreatefromstring($image);
2134 $image_p = imagecreatetruecolor($width*$ratio, $height*$ratio);
2135
2136 if ($mime == "image/png") {
2137 imagealphablending($image_p, false);
2138 imagesavealpha($image_p, true);
2139 imagealphablending($image_o, true);
2140
2141 imagecopyresampled($image_p, $image_o, 0, 0, 0, 0, ($width*$ratio), ($height*$ratio), $width, $height);
2142 ob_start();
2143 imagepng($image_p, NULL, 9);
2144 $image = ob_get_contents();
2145 ob_end_clean();
2146
2147 $ext = "png";
2148 } else {
2149 imagecopyresampled($image_p, $image_o, 0, 0, 0, 0, ($width*$ratio), ($height*$ratio), $width, $height);
2150 ob_start();
2151 imagejpeg($image_p, NULL, 80);
2152 $image = ob_get_contents();
2153 ob_end_clean();
2154
2155 $mime = "image/jpeg";
2156 $ext = "jpg";
2157 }
2158 imagedestroy($image_o);
2159 imagedestroy($image_p);
2160 }
2161 }
2162
2163 if ($ext === "") {
2164 static $mimeToExt = array (
2165 'image/jpeg' => 'jpg',
2166 'image/gif' => 'gif',
2167 'image/png' => 'png'
2168 );
2169
2170 if (isset($mimeToExt[$mime])) {
2171 $ext = $mimeToExt[$mime];
2172 }
2173 }
2174
2175 $rv = array();
2176 $rv['width'] = $width*$ratio;
2177 $rv['height'] = $height*$ratio;
2178 $rv['mime'] = $mime;
2179 $rv['image'] = $image;
2180 $rv['ext'] = $ext;
2181
2182 return $rv;
2183 }
2184
2185 /**
2186 * Get file contents, using curl if available, else file_get_contents
2187 *
2188 * @param string $source
2189 * @return bool
2190 */
2191 function getFileContents($source, $toTempFile = FALSE) {
2192 $isExternal = preg_match('#^(http|ftp)s?://#i', $source) == 1;
2193
2194 if ($isExternal && $this->isCurlInstalled) {
2195 $ch = curl_init();
2196 $outFile = NULL;
2197 $fp = NULL;
2198 $res = FALSE;
2199 $info = array('http_code' => 500);
2200
2201 curl_setopt($ch, CURLOPT_HEADER, 0);
2202 curl_setopt($ch, CURLOPT_URL, str_replace(" ","%20",$source));
2203 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
2204 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2205 curl_setopt($ch, CURLOPT_BUFFERSIZE, 4096);
2206
2207 if ($toTempFile) {
2208 $outFile = tempnam(sys_get_temp_dir(), "EPub_v" . EPub::VERSION . "_");
2209 $fp = fopen($outFile, "w+b");
2210 curl_setopt($ch, CURLOPT_FILE, $fp);
2211
2212 $res = curl_exec($ch);
2213 $info = curl_getinfo($ch);
2214
2215 curl_close($ch);
2216 fclose($fp);
2217 } else {
2218 $res = curl_exec($ch);
2219 $info = curl_getinfo($ch);
2220
2221 curl_close($ch);
2222 }
2223
2224 if ($info['http_code'] == 200 && $res != false) {
2225 if ($toTempFile) {
2226 return $outFile;
2227 }
2228 return $res;
2229 }
2230 return FALSE;
2231 }
2232
2233 if ($this->isFileGetContentsInstalled && (!$isExternal || $this->isFileGetContentsExtInstalled)) {
2234 @$data = file_get_contents($source);
2235 return $data;
2236 }
2237 return FALSE;
2238 }
2239
2240 /**
2241 * get mime type from image data
2242 *
2243 * By fireweasel found on http://stackoverflow.com/questions/2207095/get-image-mimetype-from-resource-in-php-gd
2244 * @staticvar array $type
2245 * @param object $binary
2246 * @return string
2247 */
2248 function image_file_type_from_binary($binary) {
2249 $hits = 0;
2250 if (!preg_match(
2251 '/\A(?:(\xff\xd8\xff)|(GIF8[79]a)|(\x89PNG\x0d\x0a)|(BM)|(\x49\x49(?:\x2a\x00|\x00\x4a))|(FORM.{4}ILBM))/',
2252 $binary, $hits)) {
2253 return 'application/octet-stream';
2254 }
2255 static $type = array (
2256 1 => 'image/jpeg',
2257 2 => 'image/gif',
2258 3 => 'image/png',
2259 4 => 'image/x-windows-bmp',
2260 5 => 'image/tiff',
2261 6 => 'image/x-ilbm',
2262 );
2263 return $type[count($hits) - 1];
2264 }
2265
2266 /**
2267 * @param string $source URL Source
2268 * @return string MimeType
2269 */
2270 function getMimeTypeFromUrl($source) {
2271 $ext = FALSE;
2272
2273 $srev = strrev($source);
2274 $pos = strpos($srev, "?");
2275 if ($pos !== FALSE) {
2276 $srev = substr($srev, $pos+1);
2277 }
2278
2279 $pos = strpos($srev, ".");
2280 if ($pos !== FALSE) {
2281 $ext = strtolower(strrev(substr($srev, 0, $pos)));
2282 }
2283
2284 if ($ext !== FALSE) {
2285 return $this->getMimeTypeFromExtension($ext);
2286 }
2287 return "application/octet-stream";
2288 }
2289
2290 /**
2291 * @param string $ext Extension
2292 * @return string MimeType
2293 */
2294 function getMimeTypeFromExtension($ext) {
2295 switch ($ext) {
2296 case "jpg":
2297 case "jpe":
2298 case "jpeg":
2299 return 'image/jpeg';
2300 case "gif":
2301 return 'image/gif';
2302 case "png":
2303 return 'image/png';
2304 case "bmp":
2305 return 'image/x-windows-bmp';
2306 case "tif":
2307 case "tiff":
2308 case "cpt":
2309 return 'image/tiff';
2310 case "lbm":
2311 case "ilbm":
2312 return 'image/x-ilbm';
2313 default:
2314 return "application/octet-stream";
2315 }
2316 }
2317
2318 /**
2319 * Encode html code to use html entities, safeguarding it from potential character encoding peoblems
2320 * This function is a bit different from the vanilla htmlentities function in that it does not encode html tags.
2321 *
2322 * The regexp is taken from the PHP Manual discussion, it was written by user "busbyjon".
2323 * http://www.php.net/manual/en/function.htmlentities.php#90111
2324 *
2325 * @param string $string string to encode.
2326 */
2327 public function encodeHtml($string) {
2328 $string = strtr($string, $this->html_encoding_characters);
2329
2330 //return preg_replace("/&amp;(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,5};)/", "&\\1", $string);
2331 //return preg_replace("/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,5};)/", "&amp;", $string);
2332 return $string;
2333 }
2334
2335 /**
2336 * Helper function to create a DOM fragment with given markup.
2337 *
2338 * @author Adam Schmalhofer
2339 *
2340 * @param DOMDocument $dom
2341 * @param string $markup
2342 * @return DOMNode fragment in a node.
2343 */
2344 protected function createDomFragment($dom, $markup) {
2345 $node = $dom->createDocumentFragment();
2346 $node->appendXML($markup);
2347 return $node;
2348 }
2349
2350 /**
2351 * Retrieve an array of file names currently added to the book.
2352 * $key is the filename used in the book
2353 * $value is the original filename, will be the same as $key for most entries
2354 *
2355 * @return array file list
2356 */
2357 function getFileList() {
2358 return $this->fileList;
2359 }
2360
2361 /**
2362 * @deprecated Use Zip::getRelativePath($relPath) instead.
2363 */
2364 function relPath($relPath) {
2365 die ("Function was deprecated, use Zip::getRelativePath(\$relPath); instead");
2366 }
2367
2368 /**
2369 * Set default chapter target size.
2370 * Default is 250000 bytes, and minimum is 10240 bytes.
2371 *
2372 * @param int $size segment size in bytes
2373 * @return void
2374 */
2375 function setSplitSize($size) {
2376 $this->splitDefaultSize = (int)$size;
2377 if ($size < 10240) {
2378 $this->splitDefaultSize = 10240; // Making the file smaller than 10k is not a good idea.
2379 }
2380 }
2381
2382 /**
2383 * Get the chapter target size.
2384 *
2385 * @return $size
2386 */
2387 function getSplitSize() {
2388 return $this->splitDefaultSize;
2389 }
2390
2391 /**
2392 * Remove all non essential html tags and entities.
2393 *
2394 * @global type $htmlEntities
2395 * @param string $string
2396 * @return string with the stripped entities.
2397 */
2398 function decodeHtmlEntities($string) {
2399 global $htmlEntities;
2400
2401 $string = preg_replace('~\s*<br\s*/*\s*>\s*~i', "\n", $string);
2402 $string = preg_replace('~\s*</(p|div)\s*>\s*~i', "\n\n", $string);
2403 $string = preg_replace('~<[^>]*>~', '', $string);
2404
2405 $string = strtr($string, $htmlEntities);
2406
2407 $string = str_replace('&', '&amp;', $string);
2408 $string = str_replace('&amp;amp;', '&amp;', $string);
2409 $string = preg_replace('~&amp;(#x*[a-fA-F0-9]+;)~', '&\1', $string);
2410 $string = str_replace('<', '&lt;', $string);
2411 $string = str_replace('>', '&gt;', $string);
2412
2413 return $string;
2414 }
2415
2416 /**
2417 * Simply remove all HTML tags, brute force and no finesse.
2418 *
2419 * @param string $string html
2420 * @return string
2421 */
2422 function html2text($string) {
2423 return preg_replace('~<[^>]*>~', '', $string);
2424 }
2425
2426 /**
2427 * @return string
2428 */
2429 function getLog() {
2430 return $this->log->getLog();
2431 }
2432}