diff options
Diffstat (limited to 'inc/3rdparty/libraries/PHPePub/EPub.php')
-rw-r--r-- | inc/3rdparty/libraries/PHPePub/EPub.php | 2438 |
1 files changed, 0 insertions, 2438 deletions
diff --git a/inc/3rdparty/libraries/PHPePub/EPub.php b/inc/3rdparty/libraries/PHPePub/EPub.php deleted file mode 100644 index d9b990b7..00000000 --- a/inc/3rdparty/libraries/PHPePub/EPub.php +++ /dev/null | |||
@@ -1,2438 +0,0 @@ | |||
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 <link..., <style..., and <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><img src="../images/image.png"/></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("      ", $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("/&(?![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};)/", "&", $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('&', '&', $string); | ||
2414 | $string = str_replace('&amp;', '&', $string); | ||
2415 | $string = preg_replace('~&(#x*[a-fA-F0-9]+;)~', '&\1', $string); | ||
2416 | $string = str_replace('<', '<', $string); | ||
2417 | $string = str_replace('>', '>', $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 | } | ||