aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--inc/3rdparty/libraries/PHPePub/EPub.HtmlEntities.php266
-rw-r--r--inc/3rdparty/libraries/PHPePub/EPub.NCX.php782
-rw-r--r--inc/3rdparty/libraries/PHPePub/EPub.OPF.php1226
-rw-r--r--inc/3rdparty/libraries/PHPePub/EPub.php2429
-rw-r--r--inc/3rdparty/libraries/PHPePub/EPubChapterSplitter.php201
-rw-r--r--inc/3rdparty/libraries/PHPePub/Logger.php92
-rw-r--r--inc/3rdparty/libraries/PHPePub/Zip.php818
-rw-r--r--inc/3rdparty/libraries/PHPePub/lib.uuid.LICENCE.txt31
-rw-r--r--inc/3rdparty/libraries/PHPePub/lib.uuid.php314
-rwxr-xr-xinc/poche/Poche.class.php83
-rwxr-xr-xinc/poche/global.inc.php5
-rwxr-xr-xindex.php2
-rwxr-xr-xthemes/baggy/config.twig3
-rwxr-xr-xthemes/baggy/view.twig1
14 files changed, 6253 insertions, 0 deletions
diff --git a/inc/3rdparty/libraries/PHPePub/EPub.HtmlEntities.php b/inc/3rdparty/libraries/PHPePub/EPub.HtmlEntities.php
new file mode 100644
index 00000000..376b6133
--- /dev/null
+++ b/inc/3rdparty/libraries/PHPePub/EPub.HtmlEntities.php
@@ -0,0 +1,266 @@
1<?php
2/**
3 * This should be a complete list of all HTML entities, mapped to their UTF-8 character codes.
4 *
5 * @author A. Grandt
6 * @copyright A. Grandt 2009-2013
7 * @license GNU LGPL, Attribution required for commercial implementations, requested for everything else.
8 * @version 3.00
9 */
10global $htmlEntities;
11$htmlEntities = array();
12
13$htmlEntities["&quot;"] ="\x22"; // &#34; ((double) quotation mark)
14$htmlEntities["&amp;"] ="\x26"; // &#38; (ampersand)
15$htmlEntities["&apos;"] ="\x27"; // &#39; (apostrophe = apostrophe-quote)
16$htmlEntities["&lt;"] ="\x3C"; // &#60; (less-than sign)
17$htmlEntities["&gt;"] ="\x3E"; // &#62; (greater-than sign)
18$htmlEntities["&nbsp;"] ="\xC2\xA0"; // &#160; (non-breaking space)
19$htmlEntities["&iexcl;"] ="\xC2\xA1"; // &#161; (inverted exclamation mark)
20$htmlEntities["&cent;"] ="\xC2\xA2"; // &#162; (cent)
21$htmlEntities["&pound;"] ="\xC2\xA3"; // &#163; (pound)
22$htmlEntities["&curren;"] ="\xC2\xA4"; // &#164; (currency)
23$htmlEntities["&yen;"] ="\xC2\xA5"; // &#165; (yen)
24$htmlEntities["&brvbar;"] ="\xC2\xA6"; // &#166; (broken vertical bar)
25$htmlEntities["&sect;"] ="\xC2\xA7"; // &#167; (section)
26$htmlEntities["&uml;"] ="\xC2\xA8"; // &#168; (spacing diaeresis)
27$htmlEntities["&copy;"] ="\xC2\xA9"; // &#169; (copyright)
28$htmlEntities["&ordf;"] ="\xC2\xAA"; // &#170; (feminine ordinal indicator)
29$htmlEntities["&laquo;"] ="\xC2\xAB"; // &#171; (angle quotation mark (left))
30$htmlEntities["&not;"] ="\xC2\xAC"; // &#172; (negation)
31$htmlEntities["&shy;"] ="\xC2\xAD"; // &#173; (soft hyphen)
32$htmlEntities["&reg;"] ="\xC2\xAE"; // &#174; (registered trademark)
33$htmlEntities["&macr;"] ="\xC2\xAF"; // &#175; (spacing macron)
34$htmlEntities["&deg;"] ="\xC2\xB0"; // &#176; (degree)
35$htmlEntities["&plusmn;"] ="\xC2\xB1"; // &#177; (plus-or-minus)
36$htmlEntities["&sup2;"] ="\xC2\xB2"; // &#178; (superscript 2)
37$htmlEntities["&sup3;"] ="\xC2\xB3"; // &#179; (superscript 3)
38$htmlEntities["&acute;"] ="\xC2\xB4"; // &#180; (spacing acute)
39$htmlEntities["&micro;"] ="\xC2\xB5"; // &#181; (micro)
40$htmlEntities["&para;"] ="\xC2\xB6"; // &#182; (paragraph)
41$htmlEntities["&middot;"] ="\xC2\xB7"; // &#183; (middle dot)
42$htmlEntities["&cedil;"] ="\xC2\xB8"; // &#184; (spacing cedilla)
43$htmlEntities["&sup1;"] ="\xC2\xB9"; // &#185; (superscript 1)
44$htmlEntities["&ordm;"] ="\xC2\xBA"; // &#186; (masculine ordinal indicator)
45$htmlEntities["&raquo;"] ="\xC2\xBB"; // &#187; (angle quotation mark (right))
46$htmlEntities["&frac14;"] ="\xC2\xBC"; // &#188; (fraction 1/4)
47$htmlEntities["&frac12;"] ="\xC2\xBD"; // &#189; (fraction 1/2)
48$htmlEntities["&frac34;"] ="\xC2\xBE"; // &#190; (fraction 3/4)
49$htmlEntities["&iquest;"] ="\xC2\xBF"; // &#191; (inverted question mark)
50$htmlEntities["&Agrave;"] ="\xC3\x80"; // &#192; (capital a, grave accent)
51$htmlEntities["&Aacute;"] ="\xC3\x81"; // &#193; (capital a, acute accent)
52$htmlEntities["&Acirc;"] ="\xC3\x82"; // &#194; (capital a, circumflex accent)
53$htmlEntities["&Atilde;"] ="\xC3\x83"; // &#195; (capital a, tilde)
54$htmlEntities["&Auml;"] ="\xC3\x84"; // &#196; (capital a, umlaut mark)
55$htmlEntities["&Aring;"] ="\xC3\x85"; // &#197; (capital a, ring)
56$htmlEntities["&AElig;"] ="\xC3\x86"; // &#198; (capital ae)
57$htmlEntities["&Ccedil;"] ="\xC3\x87"; // &#199; (capital c, cedilla)
58$htmlEntities["&Egrave;"] ="\xC3\x88"; // &#200; (capital e, grave accent)
59$htmlEntities["&Eacute;"] ="\xC3\x89"; // &#201; (capital e, acute accent)
60$htmlEntities["&Ecirc;"] ="\xC3\x8A"; // &#202; (capital e, circumflex accent)
61$htmlEntities["&Euml;"] ="\xC3\x8B"; // &#203; (capital e, umlaut mark)
62$htmlEntities["&Igrave;"] ="\xC3\x8C"; // &#204; (capital i, grave accent)
63$htmlEntities["&Iacute;"] ="\xC3\x8D"; // &#205; (capital i, acute accent)
64$htmlEntities["&Icirc;"] ="\xC3\x8E"; // &#206; (capital i, circumflex accent)
65$htmlEntities["&Iuml;"] ="\xC3\x8F"; // &#207; (capital i, umlaut mark)
66$htmlEntities["&ETH;"] ="\xC3\x90"; // &#208; (capital eth, Icelandic)
67$htmlEntities["&Ntilde;"] ="\xC3\x91"; // &#209; (capital n, tilde)
68$htmlEntities["&Ograve;"] ="\xC3\x92"; // &#210; (capital o, grave accent)
69$htmlEntities["&Oacute;"] ="\xC3\x93"; // &#211; (capital o, acute accent)
70$htmlEntities["&Ocirc;"] ="\xC3\x94"; // &#212; (capital o, circumflex accent)
71$htmlEntities["&Otilde;"] ="\xC3\x95"; // &#213; (capital o, tilde)
72$htmlEntities["&Ouml;"] ="\xC3\x96"; // &#214; (capital o, umlaut mark)
73$htmlEntities["&times;"] ="\xC3\x97"; // &#215; (multiplication)
74$htmlEntities["&Oslash;"] ="\xC3\x98"; // &#216; (capital o, slash)
75$htmlEntities["&Ugrave;"] ="\xC3\x99"; // &#217; (capital u, grave accent)
76$htmlEntities["&Uacute;"] ="\xC3\x9A"; // &#218; (capital u, acute accent)
77$htmlEntities["&Ucirc;"] ="\xC3\x9B"; // &#219; (capital u, circumflex accent)
78$htmlEntities["&Uuml;"] ="\xC3\x9C"; // &#220; (capital u, umlaut mark)
79$htmlEntities["&Yacute;"] ="\xC3\x9D"; // &#221; (capital y, acute accent)
80$htmlEntities["&THORN;"] ="\xC3\x9E"; // &#222; (capital THORN, Icelandic)
81$htmlEntities["&szlig;"] ="\xC3\x9F"; // &#223; (small sharp s, German)
82$htmlEntities["&agrave;"] ="\xC3\xA0"; // &#224; (small a, grave accent)
83$htmlEntities["&aacute;"] ="\xC3\xA1"; // &#225; (small a, acute accent)
84$htmlEntities["&acirc;"] ="\xC3\xA2"; // &#226; (small a, circumflex accent)
85$htmlEntities["&atilde;"] ="\xC3\xA3"; // &#227; (small a, tilde)
86$htmlEntities["&auml;"] ="\xC3\xA4"; // &#228; (small a, umlaut mark)
87$htmlEntities["&aring;"] ="\xC3\xA5"; // &#229; (small a, ring)
88$htmlEntities["&aelig;"] ="\xC3\xA6"; // &#230; (small ae)
89$htmlEntities["&ccedil;"] ="\xC3\xA7"; // &#231; (small c, cedilla)
90$htmlEntities["&egrave;"] ="\xC3\xA8"; // &#232; (small e, grave accent)
91$htmlEntities["&eacute;"] ="\xC3\xA9"; // &#233; (small e, acute accent)
92$htmlEntities["&ecirc;"] ="\xC3\xAA"; // &#234; (small e, circumflex accent)
93$htmlEntities["&euml;"] ="\xC3\xAB"; // &#235; (small e, umlaut mark)
94$htmlEntities["&igrave;"] ="\xC3\xAC"; // &#236; (small i, grave accent)
95$htmlEntities["&iacute;"] ="\xC3\xAD"; // &#237; (small i, acute accent)
96$htmlEntities["&icirc;"] ="\xC3\xAE"; // &#238; (small i, circumflex accent)
97$htmlEntities["&iuml;"] ="\xC3\xAF"; // &#239; (small i, umlaut mark)
98$htmlEntities["&eth;"] ="\xC3\xB0"; // &#240; (small eth, Icelandic)
99$htmlEntities["&ntilde;"] ="\xC3\xB1"; // &#241; (small n, tilde)
100$htmlEntities["&ograve;"] ="\xC3\xB2"; // &#242; (small o, grave accent)
101$htmlEntities["&oacute;"] ="\xC3\xB3"; // &#243; (small o, acute accent)
102$htmlEntities["&ocirc;"] ="\xC3\xB4"; // &#244; (small o, circumflex accent)
103$htmlEntities["&otilde;"] ="\xC3\xB5"; // &#245; (small o, tilde)
104$htmlEntities["&ouml;"] ="\xC3\xB6"; // &#246; (small o, umlaut mark)
105$htmlEntities["&divide;"] ="\xC3\xB7"; // &#247; (division)
106$htmlEntities["&oslash;"] ="\xC3\xB8"; // &#248; (small o, slash)
107$htmlEntities["&ugrave;"] ="\xC3\xB9"; // &#249; (small u, grave accent)
108$htmlEntities["&uacute;"] ="\xC3\xBA"; // &#250; (small u, acute accent)
109$htmlEntities["&ucirc;"] ="\xC3\xBB"; // &#251; (small u, circumflex accent)
110$htmlEntities["&uuml;"] ="\xC3\xBC"; // &#252; (small u, umlaut mark)
111$htmlEntities["&yacute;"] ="\xC3\xBD"; // &#253; (small y, acute accent)
112$htmlEntities["&thorn;"] ="\xC3\xBE"; // &#254; (small thorn, Icelandic)
113$htmlEntities["&yuml;"] ="\xC3\xBF"; // &#255; (small y, umlaut mark)
114$htmlEntities["&OElig;"] ="\xC5\x92"; // &#338; (capital ligature OE)
115$htmlEntities["&oelig;"] ="\xC5\x93"; // &#339; (small ligature oe)
116$htmlEntities["&Scaron;"] ="\xC5\xA0"; // &#352; (capital S with caron)
117$htmlEntities["&scaron;"] ="\xC5\xA1"; // &#353; (small S with caron)
118$htmlEntities["&Yuml;"] ="\xC5\xB8"; // &#376; (capital Y with diaeres)
119$htmlEntities["&fnof;"] ="\xC6\x92"; // &#402; (f with hook)
120$htmlEntities["&circ;"] ="\xCB\x86"; // &#710; (modifier letter circumflex accent)
121$htmlEntities["&tilde;"] ="\xCB\x9C"; // &#732; (small tilde)
122$htmlEntities["&Alpha;"] ="\xCE\x91"; // &#913; (Alpha)
123$htmlEntities["&Beta;"] ="\xCE\x92"; // &#914; (Beta)
124$htmlEntities["&Gamma;"] ="\xCE\x93"; // &#915; (Gamma)
125$htmlEntities["&Delta;"] ="\xCE\x94"; // &#916; (Delta)
126$htmlEntities["&Epsilon;"] ="\xCE\x95"; // &#917; (Epsilon)
127$htmlEntities["&Zeta;"] ="\xCE\x96"; // &#918; (Zeta)
128$htmlEntities["&Eta;"] ="\xCE\x97"; // &#919; (Eta)
129$htmlEntities["&Theta;"] ="\xCE\x98"; // &#920; (Theta)
130$htmlEntities["&Iota;"] ="\xCE\x99"; // &#921; (Iota)
131$htmlEntities["&Kappa;"] ="\xCE\x9A"; // &#922; (Kappa)
132$htmlEntities["&Lambda;"] ="\xCE\x9B"; // &#923; (Lambda)
133$htmlEntities["&Mu;"] ="\xCE\x9C"; // &#924; (Mu)
134$htmlEntities["&Nu;"] ="\xCE\x9D"; // &#925; (Nu)
135$htmlEntities["&Xi;"] ="\xCE\x9E"; // &#926; (Xi)
136$htmlEntities["&Omicron;"] ="\xCE\x9F"; // &#927; (Omicron)
137$htmlEntities["&Pi;"] ="\xCE\xA0"; // &#928; (Pi)
138$htmlEntities["&Rho;"] ="\xCE\xA1"; // &#929; (Rho)
139$htmlEntities["&Sigma;"] ="\xCE\xA3"; // &#931; (Sigma)
140$htmlEntities["&Tau;"] ="\xCE\xA4"; // &#932; (Tau)
141$htmlEntities["&Upsilon;"] ="\xCE\xA5"; // &#933; (Upsilon)
142$htmlEntities["&Phi;"] ="\xCE\xA6"; // &#934; (Phi)
143$htmlEntities["&Chi;"] ="\xCE\xA7"; // &#935; (Chi)
144$htmlEntities["&Psi;"] ="\xCE\xA8"; // &#936; (Psi)
145$htmlEntities["&Omega;"] ="\xCE\xA9"; // &#937; (Omega)
146$htmlEntities["&alpha;"] ="\xCE\xB1"; // &#945; (alpha)
147$htmlEntities["&beta;"] ="\xCE\xB2"; // &#946; (beta)
148$htmlEntities["&gamma;"] ="\xCE\xB3"; // &#947; (gamma)
149$htmlEntities["&delta;"] ="\xCE\xB4"; // &#948; (delta)
150$htmlEntities["&epsilon;"] ="\xCE\xB5"; // &#949; (epsilon)
151$htmlEntities["&zeta;"] ="\xCE\xB6"; // &#950; (zeta)
152$htmlEntities["&eta;"] ="\xCE\xB7"; // &#951; (eta)
153$htmlEntities["&theta;"] ="\xCE\xB8"; // &#952; (theta)
154$htmlEntities["&iota;"] ="\xCE\xB9"; // &#953; (iota)
155$htmlEntities["&kappa;"] ="\xCE\xBA"; // &#954; (kappa)
156$htmlEntities["&lambda;"] ="\xCE\xBB"; // &#955; (lambda)
157$htmlEntities["&mu;"] ="\xCE\xBC"; // &#956; (mu)
158$htmlEntities["&nu;"] ="\xCE\xBD"; // &#957; (nu)
159$htmlEntities["&xi;"] ="\xCE\xBE"; // &#958; (xi)
160$htmlEntities["&omicron;"] ="\xCE\xBF"; // &#959; (omicron)
161$htmlEntities["&pi;"] ="\xCF\x80"; // &#960; (pi)
162$htmlEntities["&rho;"] ="\xCF\x81"; // &#961; (rho)
163$htmlEntities["&sigmaf;"] ="\xCF\x82"; // &#962; (sigmaf)
164$htmlEntities["&sigma;"] ="\xCF\x83"; // &#963; (sigma)
165$htmlEntities["&tau;"] ="\xCF\x84"; // &#964; (tau)
166$htmlEntities["&upsilon;"] ="\xCF\x85"; // &#965; (upsilon)
167$htmlEntities["&phi;"] ="\xCF\x86"; // &#966; (phi)
168$htmlEntities["&chi;"] ="\xCF\x87"; // &#967; (chi)
169$htmlEntities["&psi;"] ="\xCF\x88"; // &#968; (psi)
170$htmlEntities["&omega;"] ="\xCF\x89"; // &#969; (omega)
171$htmlEntities["&thetasym;"] ="\xCF\x91"; // &#977; (theta symbol)
172$htmlEntities["&upsih;"] ="\xCF\x92"; // &#978; (upsilon symbol)
173$htmlEntities["&piv;"] ="\xCF\x96"; // &#982; (pi symbol)
174$htmlEntities["&ensp;"] ="\xE2\x80\x82"; // &#8194; (en space)
175$htmlEntities["&emsp;"] ="\xE2\x80\x83"; // &#8195; (em space)
176$htmlEntities["&thinsp;"] ="\xE2\x80\x89"; // &#8201; (thin space)
177$htmlEntities["&zwnj;"] ="‌\xE2\x80\x8C"; // &#8204; (zero width non-joiner)
178$htmlEntities["&zwj;"] ="\xE2\x80\x8D‍"; // &#8205; (zero width joiner)
179$htmlEntities["&lrm;"] ="‎\xE2\x80\x8E"; // &#8206; (left-to-right mark)
180$htmlEntities["&rlm;"] ="\xE2\x80\x8F"; // &#8207; (right-to-left mark)
181$htmlEntities["&ndash;"] ="\xE2\x80\x93"; // &#8211; (en dash)
182$htmlEntities["&mdash;"] ="\xE2\x80\x94"; // &#8212; (em dash)
183$htmlEntities["&lsquo;"] ="\xE2\x80\x98"; // &#8216; (left single quotation mark)
184$htmlEntities["&rsquo;"] ="\xE2\x80\x99"; // &#8217; (right single quotation mark)
185$htmlEntities["&sbquo;"] ="\xE2\x80\x9A"; // &#8218; (single low-9 quotation mark)
186$htmlEntities["&ldquo;"] ="\xE2\x80\x9C"; // &#8220; (left double quotation mark)
187$htmlEntities["&rdquo;"] ="\xE2\x80\x9D"; // &#8221; (right double quotation mark)
188$htmlEntities["&bdquo;"] ="\xE2\x80\x9E"; // &#8222; (double low-9 quotation mark)
189$htmlEntities["&dagger;"] ="\xE2\x80\xA0"; // &#8224; (dagger)
190$htmlEntities["&Dagger;"] ="\xE2\x80\xA1"; // &#8225; (double dagger)
191$htmlEntities["&bull;"] ="\xE2\x80\xA2"; // &#8226; (bullet)
192$htmlEntities["&hellip;"] ="\xE2\x80\xA6"; // &#8230; (horizontal ellipsis)
193$htmlEntities["&permil;"] ="\xE2\x80\xB0"; // &#8240; (per mille)
194$htmlEntities["&prime;"] ="\xE2\x80\xB2"; // &#8242; (minutes or prime)
195$htmlEntities["&Prime;"] ="\xE2\x80\xB3"; // &#8243; (seconds or Double Prime)
196$htmlEntities["&lsaquo;"] ="\xE2\x80\xB9"; // &#8249; (single left angle quotation)
197$htmlEntities["&rsaquo;"] ="\xE2\x80\xBA"; // &#8250; (single right angle quotation)
198$htmlEntities["&oline;"] ="\xE2\x80\xBE"; // &#8254; (overline)
199$htmlEntities["&frasl;"] ="\xE2\x81\x84"; // &#8260; (fraction slash)
200$htmlEntities["&euro;"] ="\xE2\x82\xAC"; // &#8364; (euro)
201$htmlEntities["&image;"] ="\xE2\x84\x91"; // &#8465; (blackletter capital I)
202$htmlEntities["&weierp;"] ="\xE2\x84\x98"; // &#8472; (script capital P)
203$htmlEntities["&real;"] ="\xE2\x84\x9C"; // &#8476; (blackletter capital R)
204$htmlEntities["&trade;"] ="\xE2\x84\xA2"; // &#8482; (trademark)
205$htmlEntities["&alefsym;"] ="\xE2\x84\xB5"; // &#8501; (alef)
206$htmlEntities["&larr;"] ="\xE2\x86\x90"; // &#8592; (left arrow)
207$htmlEntities["&uarr;"] ="\xE2\x86\x91"; // &#8593; (up arrow)
208$htmlEntities["&rarr;"] ="\xE2\x86\x92"; // &#8594; (right arrow)
209$htmlEntities["&darr;"] ="\xE2\x86\x93"; // &#8595; (down arrow)
210$htmlEntities["&harr;"] ="\xE2\x86\x94"; // &#8596; (left right arrow)
211$htmlEntities["&crarr;"] ="\xE2\x86\xB5"; // &#8629; (carriage return arrow)
212$htmlEntities["&lArr;"] ="\xE2\x87\x90"; // &#8656; (left double arrow)
213$htmlEntities["&uArr;"] ="\xE2\x87\x91"; // &#8657; (up double arrow)
214$htmlEntities["&rArr;"] ="\xE2\x87\x92"; // &#8658; (right double arrow)
215$htmlEntities["&dArr;"] ="\xE2\x87\x93"; // &#8659; (down double arrow)
216$htmlEntities["&hArr;"] ="\xE2\x87\x94"; // &#8660; (left right double arrow)
217$htmlEntities["&forall;"] ="\xE2\x88\x80"; // &#8704; (for all)
218$htmlEntities["&part;"] ="\xE2\x88\x82"; // &#8706; (partial differential)
219$htmlEntities["&exist;"] ="\xE2\x88\x83"; // &#8707; (there exists)
220$htmlEntities["&empty;"] ="\xE2\x88\x85"; // &#8709; (empty set)
221$htmlEntities["&nabla;"] ="\xE2\x88\x87"; // &#8711; (backward difference)
222$htmlEntities["&isin;"] ="\xE2\x88\x88"; // &#8712; (element of)
223$htmlEntities["&notin;"] ="\xE2\x88\x89"; // &#8713; (not an element of)
224$htmlEntities["&ni;"] ="\xE2\x88\x8B"; // &#8715; (ni = contains as member)
225$htmlEntities["&prod;"] ="\xE2\x88\x8F"; // &#8719; (n-ary product)
226$htmlEntities["&sum;"] ="\xE2\x88\x91"; // &#8721; (n-ary sumation)
227$htmlEntities["&minus;"] ="\xE2\x88\x92"; // &#8722; (minus)
228$htmlEntities["&lowast;"] ="\xE2\x88\x97"; // &#8727; (asterisk operator)
229$htmlEntities["&radic;"] ="\xE2\x88\x9A"; // &#8730; (square root)
230$htmlEntities["&prop;"] ="\xE2\x88\x9D"; // &#8733; (proportional to)
231$htmlEntities["&infin;"] ="\xE2\x88\x9E"; // &#8734; (infinity)
232$htmlEntities["&ang;"] ="\xE2\x88\xA0"; // &#8736; (angle)
233$htmlEntities["&and;"] ="\xE2\x88\xA7"; // &#8743; (logical and)
234$htmlEntities["&or;"] ="\xE2\x88\xA8"; // &#8744; (logical or)
235$htmlEntities["&cap;"] ="\xE2\x88\xA9"; // &#8745; (intersection)
236$htmlEntities["&cup;"] ="\xE2\x88\xAA"; // &#8746; (union)
237$htmlEntities["&int;"] ="\xE2\x88\xAB"; // &#8747; (integral)
238$htmlEntities["&there4;"] ="\xE2\x88\xB4"; // &#8756; (therefore)
239$htmlEntities["&sim;"] ="\xE2\x88\xBC"; // &#8764; (similar to)
240$htmlEntities["&cong;"] ="\xE2\x89\x85"; // &#8773; (congruent to)
241$htmlEntities["&asymp;"] ="\xE2\x89\x88"; // &#8776; (approximately equal)
242$htmlEntities["&ne;"] ="\xE2\x89\xA0"; // &#8800; (not equal)
243$htmlEntities["&equiv;"] ="\xE2\x89\xA1"; // &#8801; (equivalent)
244$htmlEntities["&le;"] ="\xE2\x89\xA4"; // &#8804; (less or equal)
245$htmlEntities["&ge;"] ="\xE2\x89\xA5"; // &#8805; (greater or equal)
246$htmlEntities["&sub;"] ="\xE2\x8A\x82"; // &#8834; (subset of)
247$htmlEntities["&sup;"] ="\xE2\x8A\x83"; // &#8835; (superset of)
248$htmlEntities["&nsub;"] ="\xE2\x8A\x84"; // &#8836; (not subset of)
249$htmlEntities["&sube;"] ="\xE2\x8A\x86"; // &#8838; (subset or equal)
250$htmlEntities["&supe;"] ="\xE2\x8A\x87"; // &#8839; (superset or equal)
251$htmlEntities["&oplus;"] ="\xE2\x8A\x95"; // &#8853; (circled plus)
252$htmlEntities["&otimes;"] ="\xE2\x8A\x87"; // &#8855; (circled times)
253$htmlEntities["&perp;"] ="\xE2\x8A\xA5"; // &#8869; (perpendicular)
254$htmlEntities["&sdot;"] ="\xE2\x8C\x85"; // &#8901; (dot operator)
255$htmlEntities["&lceil;"] ="\xE2\x8C\x88"; // &#8968; (left ceiling)
256$htmlEntities["&rceil;"] ="\xE2\x8C\x89"; // &#8969; (right ceiling)
257$htmlEntities["&lfloor;"] ="\xE2\x8C\x8A"; // &#8970; (left floor)
258$htmlEntities["&rfloor;"] ="\xE2\x8C\x8B"; // &#8971; (right floor)
259$htmlEntities["&lang;"] ="\xE2\x8C\xA9"; // &#9001; (left angle bracket = bra)
260$htmlEntities["&rang;"] ="\xE2\x8C\xAA"; // &#9002; (right angle bracket = ket)
261$htmlEntities["&loz;"] ="\xE2\x97\x8A"; // &#9674; (lozenge)
262$htmlEntities["&spades;"] ="\xE2\x99\xA0"; // &#9824; (spade)
263$htmlEntities["&clubs;"] ="\xE2\x99\xA3"; // &#9827; (club)
264$htmlEntities["&hearts;"] ="\xE2\x99\xA5"; // &#9829; (heart)
265$htmlEntities["&diams;"] ="\xE2\x99\xA6"; // &#9830; (diamond)
266?> \ No newline at end of file
diff --git a/inc/3rdparty/libraries/PHPePub/EPub.NCX.php b/inc/3rdparty/libraries/PHPePub/EPub.NCX.php
new file mode 100644
index 00000000..e5da05cd
--- /dev/null
+++ b/inc/3rdparty/libraries/PHPePub/EPub.NCX.php
@@ -0,0 +1,782 @@
1<?php
2/**
3 * ePub NCX file structure
4 *
5 * @author A. Grandt <php@grandt.com>
6 * @copyright 2009-2014 A. Grandt
7 * @license GNU LGPL, Attribution required for commercial implementations, requested for everything else.
8 * @version 3.20
9 */
10class Ncx {
11 const _VERSION = 3.20;
12
13 const MIMETYPE = "application/x-dtbncx+xml";
14
15 private $bookVersion = EPub::BOOK_VERSION_EPUB2;
16
17 private $navMap = NULL;
18 private $uid = NULL;
19 private $meta = array();
20 private $docTitle = NULL;
21 private $docAuthor = NULL;
22
23 private $currentLevel = NULL;
24 private $lastLevel = NULL;
25
26 private $languageCode = "en";
27 private $writingDirection = EPub::DIRECTION_LEFT_TO_RIGHT;
28
29 public $chapterList = array();
30 public $referencesTitle = "Guide";
31 public $referencesClass = "references";
32 public $referencesId = "references";
33 public $referencesList = array();
34 public $referencesName = array();
35 public $referencesOrder = NULL;
36
37 /**
38 * Class constructor.
39 *
40 * @param string $uid
41 * @param string $docTitle
42 * @param string $docAuthor
43 * @param string $languageCode
44 * @param string $writingDirection
45 */
46 function __construct($uid = NULL, $docTitle = NULL, $docAuthor = NULL, $languageCode = "en", $writingDirection = EPub::DIRECTION_LEFT_TO_RIGHT) {
47 $this->navMap = new NavMap($writingDirection);
48 $this->currentLevel = $this->navMap;
49 $this->setUid($uid);
50 $this->setDocTitle($docTitle);
51 $this->setDocAuthor($docAuthor);
52 $this->setLanguageCode($languageCode);
53 $this->setWritingDirection($writingDirection);
54 }
55
56 /**
57 * Class destructor
58 *
59 * @return void
60 */
61 function __destruct() {
62 unset($this->bookVersion, $this->navMap, $this->uid, $this->meta);
63 unset($this->docTitle, $this->docAuthor, $this->currentLevel, $this->lastLevel);
64 unset($this->languageCode, $this->writingDirection, $this->chapterList, $this->referencesTitle);
65 unset($this->referencesClass, $this->referencesId, $this->referencesList, $this->referencesName);
66 unset($this->referencesOrder);
67 }
68
69 /**
70 *
71 * Enter description here ...
72 *
73 * @param string $bookVersion
74 */
75 function setVersion($bookVersion) {
76 $this->bookVersion = is_string($bookVersion) ? trim($bookVersion) : EPub::BOOK_VERSION_EPUB2;
77 }
78
79 /**
80 *
81 * @return bool TRUE if the book is set to type ePub 2
82 */
83 function isEPubVersion2() {
84 return $this->bookVersion === EPub::BOOK_VERSION_EPUB2;
85 }
86
87 /**
88 *
89 * Enter description here ...
90 *
91 * @param string $uid
92 */
93 function setUid($uid) {
94 $this->uid = is_string($uid) ? trim($uid) : NULL;
95 }
96
97 /**
98 *
99 * Enter description here ...
100 *
101 * @param string $docTitle
102 */
103 function setDocTitle($docTitle) {
104 $this->docTitle = is_string($docTitle) ? trim($docTitle) : NULL;
105 }
106
107 /**
108 *
109 * Enter description here ...
110 *
111 * @param string $docAuthor
112 */
113 function setDocAuthor($docAuthor) {
114 $this->docAuthor = is_string($docAuthor) ? trim($docAuthor) : NULL;
115 }
116
117 /**
118 *
119 * Enter description here ...
120 *
121 * @param string $languageCode
122 */
123 function setLanguageCode($languageCode) {
124 $this->languageCode = is_string($languageCode) ? trim($languageCode) : "en";
125 }
126
127 /**
128 *
129 * Enter description here ...
130 *
131 * @param string $writingDirection
132 */
133 function setWritingDirection($writingDirection) {
134 $this->writingDirection = is_string($writingDirection) ? trim($writingDirection) : EPub::DIRECTION_LEFT_TO_RIGHT;
135 }
136
137 /**
138 *
139 * Enter description here ...
140 *
141 * @param NavMap $navMap
142 */
143 function setNavMap($navMap) {
144 if ($navMap != NULL && is_object($navMap) && get_class($navMap) === "NavMap") {
145 $this->navMap = $navMap;
146 }
147 }
148
149 /**
150 * Add one chapter level.
151 *
152 * Subsequent chapters will be added to this level.
153 *
154 * @param string $navTitle
155 * @param string $navId
156 * @param string $navClass
157 * @param string $isNavHidden
158 * @param string $writingDirection
159 * @return NavPoint
160 */
161 function subLevel($navTitle = NULL, $navId = NULL, $navClass = NULL, $isNavHidden = FALSE, $writingDirection = NULL) {
162 $navPoint = FALSE;
163 if (isset($navTitle) && isset($navClass)) {
164 $navPoint = new NavPoint($navTitle, NULL, $navId, $navClass, $isNavHidden, $writingDirection);
165 $this->addNavPoint($navPoint);
166 }
167 if ($this->lastLevel !== NULL) {
168 $this->currentLevel = $this->lastLevel;
169 }
170 return $navPoint;
171 }
172
173 /**
174 * Step back one chapter level.
175 *
176 * Subsequent chapters will be added to this chapters parent level.
177 */
178 function backLevel() {
179 $this->lastLevel = $this->currentLevel;
180 $this->currentLevel = $this->currentLevel->getParent();
181 }
182
183 /**
184 * Step back to the root level.
185 *
186 * Subsequent chapters will be added to the rooot NavMap.
187 */
188 function rootLevel() {
189 $this->lastLevel = $this->currentLevel;
190 $this->currentLevel = $this->navMap;
191 }
192
193 /**
194 * Step back to the given level.
195 * Useful for returning to a previous level from deep within the structure.
196 * Values below 2 will have the same effect as rootLevel()
197 *
198 * @param int $newLevel
199 */
200 function setCurrentLevel($newLevel) {
201 if ($newLevel <= 1) {
202 $this->rootLevel();
203 } else {
204 while ($this->currentLevel->getLevel() > $newLevel) {
205 $this->backLevel();
206 }
207 }
208 }
209
210 /**
211 * Get current level count.
212 * The indentation of the current structure point.
213 *
214 * @return current level count;
215 */
216 function getCurrentLevel() {
217 return $this->currentLevel->getLevel();
218 }
219
220 /**
221 * Add child NavPoints to current level.
222 *
223 * @param NavPoint $navPoint
224 */
225 function addNavPoint($navPoint) {
226 $this->lastLevel = $this->currentLevel->addNavPoint($navPoint);
227 }
228
229 /**
230 *
231 * Enter description here ...
232 *
233 * @return NavMap
234 */
235 function getNavMap() {
236 return $this->navMap;
237 }
238
239 /**
240 *
241 * Enter description here ...
242 *
243 * @param string $name
244 * @param string $content
245 */
246 function addMetaEntry($name, $content) {
247 $name = is_string($name) ? trim($name) : NULL;
248 $content = is_string($content) ? trim($content) : NULL;
249
250 if ($name != NULL && $content != NULL) {
251 $this->meta[] = array($name => $content);
252 }
253 }
254
255 /**
256 *
257 * Enter description here ...
258 *
259 * @return string
260 */
261 function finalize() {
262 $nav = $this->navMap->finalize();
263
264 $ncx = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
265 if ($this->isEPubVersion2()) {
266 $ncx .= "<!DOCTYPE ncx PUBLIC \"-//NISO//DTD ncx 2005-1//EN\"\n"
267 . " \"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd\">\n";
268 }
269 $ncx .= "<ncx xmlns=\"http://www.daisy.org/z3986/2005/ncx/\" version=\"2005-1\" xml:lang=\"" . $this->languageCode . "\" dir=\"" . $this->writingDirection . "\">\n"
270 . "\t<head>\n"
271 . "\t\t<meta name=\"dtb:uid\" content=\"" . $this->uid . "\" />\n"
272 . "\t\t<meta name=\"dtb:depth\" content=\"" . $this->navMap->getNavLevels() . "\" />\n"
273 . "\t\t<meta name=\"dtb:totalPageCount\" content=\"0\" />\n"
274 . "\t\t<meta name=\"dtb:maxPageNumber\" content=\"0\" />\n";
275
276 if (sizeof($this->meta)) {
277 foreach ($this->meta as $metaEntry) {
278 list($name, $content) = each($metaEntry);
279 $ncx .= "\t\t<meta name=\"" . $name . "\" content=\"" . $content . "\" />\n";
280 }
281 }
282
283 $ncx .= "\t</head>\n\n\t<docTitle>\n\t\t<text>"
284 . $this->docTitle
285 . "</text>\n\t</docTitle>\n\n\t<docAuthor>\n\t\t<text>"
286 . $this->docAuthor
287 . "</text>\n\t</docAuthor>\n\n"
288 . $nav;
289
290 return $ncx . "</ncx>\n";
291 }
292
293 /**
294 *
295 * @param string $title
296 * @param string $cssFileName
297 * @return string
298 */
299 function finalizeEPub3($title = "Table of Contents", $cssFileName = NULL) {
300 $end = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
301 . "<html xmlns=\"http://www.w3.org/1999/xhtml\"\n"
302 . " xmlns:epub=\"http://www.idpf.org/2007/ops\"\n"
303 . " xml:lang=\"" . $this->languageCode . "\" lang=\"" . $this->languageCode . "\" dir=\"" . $this->writingDirection . "\">\n"
304 . "\t<head>\n"
305 . "\t\t<title>" . $this->docTitle . "</title>\n"
306 . "\t\t<meta http-equiv=\"default-style\" content=\"text/html; charset=utf-8\"/>\n";
307 if ($cssFileName !== NULL) {
308 $end .= "\t\t<link rel=\"stylesheet\" href=\"" . $cssFileName . "\" type=\"text/css\"/>\n";
309 }
310 $end .= "\t</head>\n"
311 . "\t<body epub:type=\"frontmatter toc\">\n"
312 . "\t\t<header>\n"
313 . "\t\t\t<h1>" . $title . "</h1>\n"
314 . "\t\t</header>\n"
315 . $this->navMap->finalizeEPub3()
316 . $this->finalizeEPub3Landmarks()
317 . "\t</body>\n"
318 . "</html>\n";
319
320 return $end;
321 }
322
323 /**
324 * Build the references for the ePub 2 toc.
325 * These are merely reference pages added to the end of the navMap though.
326 *
327 * @return string
328 */
329 function finalizeReferences() {
330 if (isset($this->referencesList) && sizeof($this->referencesList) > 0) {
331 $this->rootLevel();
332 $this->subLevel($this->referencesTitle, $this->referencesId, $this->referencesClass);
333 $refId = 1;
334 while (list($item, $descriptive) = each($this->referencesOrder)) {
335 if (array_key_exists($item, $this->referencesList)) {
336 $name = (empty($this->referencesName[$item]) ? $descriptive : $this->referencesName[$item]);
337 $navPoint = new NavPoint($name, $this->referencesList[$item], "ref-" . $refId++);
338 $this->addNavPoint($navPoint);
339 }
340 }
341 }
342 }
343
344 /**
345 * Build the landmarks for the ePub 3 toc.
346 * @return string
347 */
348 function finalizeEPub3Landmarks() {
349 $lm = "";
350 if (isset($this->referencesList) && sizeof($this->referencesList) > 0) {
351 $lm = "\t\t\t<nav epub:type=\"landmarks\">\n"
352 . "\t\t\t\t<h2"
353 . ($this->writingDirection === EPub::DIRECTION_RIGHT_TO_LEFT ? " dir=\"rtl\"" : "")
354 . ">" . $this->referencesTitle . "</h2>\n"
355 . "\t\t\t\t<ol>\n";
356
357 $li = "";
358 while (list($item, $descriptive) = each($this->referencesOrder)) {
359 if (array_key_exists($item, $this->referencesList)) {
360 $li .= "\t\t\t\t\t<li><a epub:type=\""
361 . $item
362 . "\" href=\"" . $this->referencesList[$item] . "\">"
363 . (empty($this->referencesName[$item]) ? $descriptive : $this->referencesName[$item])
364 . "</a></li>\n";
365 }
366 }
367 if (empty($li)) {
368 return "";
369 }
370
371 $lm .= $li
372 . "\t\t\t\t</ol>\n"
373 . "\t\t\t</nav>\n";
374 }
375 return $lm;
376 }
377}
378
379/**
380 * ePub NavMap class
381 */
382class NavMap {
383 const _VERSION = 3.00;
384
385 private $navPoints = array();
386 private $navLevels = 0;
387 private $writingDirection = NULL;
388
389 /**
390 * Class constructor.
391 *
392 * @return void
393 */
394 function __construct($writingDirection = NULL) {
395 $this->setWritingDirection($writingDirection);
396 }
397
398 /**
399 * Class destructor
400 *
401 * @return void
402 */
403 function __destruct() {
404 unset($this->navPoints, $this->navLevels, $this->writingDirection);
405 }
406
407 /**
408 * Set the writing direction to be used for this NavPoint.
409 *
410 * @param string $writingDirection
411 */
412 function setWritingDirection($writingDirection) {
413 $this->writingDirection = isset($writingDirection) && is_string($writingDirection) ? trim($writingDirection) : NULL;
414 }
415
416 function getWritingDirection() {
417 return $this->writingDirection;
418 }
419
420 /**
421 * Add a navPoint to the root of the NavMap.
422 *
423 * @param NavPoint $navPoint
424 * @return NavMap
425 */
426 function addNavPoint($navPoint) {
427 if ($navPoint != NULL && is_object($navPoint) && get_class($navPoint) === "NavPoint") {
428 $navPoint->setParent($this);
429 if ($navPoint->getWritingDirection() == NULL) {
430 $navPoint->setWritingDirection($this->writingDirection);
431 }
432 $this->navPoints[] = $navPoint;
433 return $navPoint;
434 }
435 return $this;
436 }
437
438 /**
439 * The final max depth for the "dtb:depth" meta attribute
440 * Only available after finalize have been called.
441 *
442 * @return number
443 */
444 function getNavLevels() {
445 return $this->navLevels+1;
446 }
447
448 function getLevel() {
449 return 1;
450 }
451
452 function getParent() {
453 return $this;
454 }
455
456 /**
457 * Finalize the navMap, the final max depth for the "dtb:depth" meta attribute can be retrieved with getNavLevels after finalization
458 *
459 */
460 function finalize() {
461 $playOrder = 0;
462 $this->navLevels = 0;
463
464 $nav = "\t<navMap>\n";
465 if (sizeof($this->navPoints) > 0) {
466 $this->navLevels++;
467 foreach ($this->navPoints as $navPoint) {
468 $retLevel = $navPoint->finalize($nav, $playOrder, 0);
469 if ($retLevel > $this->navLevels) {
470 $this->navLevels = $retLevel;
471 }
472 }
473 }
474 return $nav . "\t</navMap>\n";
475 }
476
477 /**
478 * Finalize the navMap, the final max depth for the "dtb:depth" meta attribute can be retrieved with getNavLevels after finalization
479 *
480 */
481 function finalizeEPub3() {
482 $playOrder = 0;
483 $level = 0;
484 $this->navLevels = 0;
485
486 $nav = "\t\t<nav epub:type=\"toc\" id=\"toc\">\n";
487
488 if (sizeof($this->navPoints) > 0) {
489 $this->navLevels++;
490
491 $nav .= str_repeat("\t", $level) . "\t\t\t<ol epub:type=\"list\">\n";
492 foreach ($this->navPoints as $navPoint) {
493 $retLevel = $navPoint->finalizeEPub3($nav, $playOrder, 0);
494 if ($retLevel > $this->navLevels) {
495 $this->navLevels = $retLevel;
496 }
497 }
498 $nav .= str_repeat("\t", $level) . "\t\t\t</ol>\n";
499 }
500
501 return $nav . "\t\t</nav>\n";
502 }
503}
504
505/**
506 * ePub NavPoint class
507 */
508class NavPoint {
509 const _VERSION = 3.00;
510
511 private $label = NULL;
512 private $contentSrc = NULL;
513 private $id = NULL;
514 private $navClass = NULL;
515 private $isNavHidden = FALSE;
516 private $navPoints = array();
517 private $parent = NULL;
518
519 /**
520 * Class constructor.
521 *
522 * All three attributes are mandatory, though if ID is set to null (default) the value will be generated.
523 *
524 * @param string $label
525 * @param string $contentSrc
526 * @param string $id
527 * @param string $navClass
528 * @param bool $isNavHidden
529 * @param string $writingDirection
530 */
531 function __construct($label, $contentSrc = NULL, $id = NULL, $navClass = NULL, $isNavHidden = FALSE, $writingDirection = NULL) {
532 $this->setLabel($label);
533 $this->setContentSrc($contentSrc);
534 $this->setId($id);
535 $this->setNavClass($navClass);
536 $this->setNavHidden($isNavHidden);
537 $this->setWritingDirection($writingDirection);
538 }
539
540 /**
541 * Class destructor
542 *
543 * @return void
544 */
545 function __destruct() {
546 unset($this->label, $this->contentSrc, $this->id, $this->navClass);
547 unset($this->isNavHidden, $this->navPoints, $this->parent);
548 }
549
550 /**
551 * Set the Text label for the NavPoint.
552 *
553 * The label is mandatory.
554 *
555 * @param string $label
556 */
557 function setLabel($label) {
558 $this->label = is_string($label) ? trim($label) : NULL;
559 }
560
561 /**
562 * Get the Text label for the NavPoint.
563 *
564 * @return string Label
565 */
566 function getLabel() {
567 return $this->label;
568 }
569
570 /**
571 * Set the src reference for the NavPoint.
572 *
573 * The src is mandatory for ePub 2.
574 *
575 * @param string $contentSrc
576 */
577 function setContentSrc($contentSrc) {
578 $this->contentSrc = isset($contentSrc) && is_string($contentSrc) ? trim($contentSrc) : NULL;
579 }
580
581 /**
582 * Get the src reference for the NavPoint.
583 *
584 * @return string content src url.
585 */
586 function getContentSrc() {
587 return $this->contentSrc;
588 }
589 /**
590 * Set the parent for this NavPoint.
591 *
592 * @param NavPoint or NavMap $parent
593 */
594 function setParent($parent) {
595 if ($parent != NULL && is_object($parent) &&
596 (get_class($parent) === "NavPoint" || get_class($parent) === "NavMap") ) {
597 $this->parent = $parent;
598 }
599 }
600
601 /**
602 * Get the parent to this NavPoint.
603 *
604 * @return NavPoint, or NavMap if the parent is the root.
605 */
606 function getParent() {
607 return $this->parent;
608 }
609
610 /**
611 * Get the current level. 1 = document root.
612 *
613 * @return int level
614 */
615 function getLevel() {
616 return $this->parent === NULL ? 1 : $this->parent->getLevel()+1;
617 }
618
619 /**
620 * Set the id for the NavPoint.
621 *
622 * The id must be unique, and is mandatory.
623 *
624 * @param string $id
625 */
626 function setId($id) {
627 $this->id = is_string($id) ? trim($id) : NULL;
628 }
629
630 /**
631 * Set the class to be used for this NavPoint.
632 *
633 * @param string $navClass
634 */
635 function setNavClass($navClass) {
636 $this->navClass = isset($navClass) && is_string($navClass) ? trim($navClass) : NULL;
637 }
638
639 /**
640 * Set the class to be used for this NavPoint.
641 *
642 * @param string $navClass
643 */
644 function setNavHidden($isNavHidden) {
645 $this->isNavHidden = $isNavHidden === TRUE;
646 }
647
648 /**
649 * Set the writing direction to be used for this NavPoint.
650 *
651 * @param string $writingDirection
652 */
653 function setWritingDirection($writingDirection) {
654 $this->writingDirection = isset($writingDirection) && is_string($writingDirection) ? trim($writingDirection) : NULL;
655 }
656
657 function getWritingDirection() {
658 return $this->writingDirection;
659 }
660
661 /**
662 * Add child NavPoints for multi level NavMaps.
663 *
664 * @param NavPoint $navPoint
665 */
666 function addNavPoint($navPoint) {
667 if ($navPoint != NULL && is_object($navPoint) && get_class($navPoint) === "NavPoint") {
668 $navPoint->setParent($this);
669 if ($navPoint->getWritingDirection() == NULL) {
670 $navPoint->setWritingDirection($this->writingDirection);
671 }
672 $this->navPoints[] = $navPoint;
673 return $navPoint;
674 }
675 return $this;
676 }
677
678 /**
679 *
680 * Enter description here ...
681 *
682 * @param string $nav
683 * @param int $playOrder
684 * @param int $level
685 * @return int
686 */
687 function finalize(&$nav = "", &$playOrder = 0, $level = 0) {
688 $maxLevel = $level;
689 $levelAdjust = 0;
690
691 if ($this->isNavHidden) {
692 return $maxLevel;
693 }
694
695 if (isset($this->contentSrc)) {
696 $playOrder++;
697
698 if ($this->id == NULL) {
699 $this->id = "navpoint-" . $playOrder;
700 }
701 $nav .= str_repeat("\t", $level) . "\t\t<navPoint id=\"" . $this->id . "\" playOrder=\"" . $playOrder . "\">\n"
702 . str_repeat("\t", $level) . "\t\t\t<navLabel>\n"
703 . str_repeat("\t", $level) . "\t\t\t\t<text>" . $this->label . "</text>\n"
704 . str_repeat("\t", $level) . "\t\t\t</navLabel>\n"
705 . str_repeat("\t", $level) . "\t\t\t<content src=\"" . $this->contentSrc . "\" />\n";
706 } else {
707 $levelAdjust++;
708 }
709
710 if (sizeof($this->navPoints) > 0) {
711 $maxLevel++;
712 foreach ($this->navPoints as $navPoint) {
713 $retLevel = $navPoint->finalize($nav, $playOrder, ($level+1+$levelAdjust));
714 if ($retLevel > $maxLevel) {
715 $maxLevel = $retLevel;
716 }
717 }
718 }
719
720 if (isset($this->contentSrc)) {
721 $nav .= str_repeat("\t", $level) . "\t\t</navPoint>\n";
722 }
723
724 return $maxLevel;
725 }
726
727 /**
728 *
729 * Enter description here ...
730 *
731 * @param string $nav
732 * @param int $playOrder
733 * @param int $level
734 * @return int
735 */
736 function finalizeEPub3(&$nav = "", &$playOrder = 0, $level = 0, $subLevelClass = NULL, $subLevelHidden = FALSE) {
737 $maxLevel = $level;
738
739 if ($this->id == NULL) {
740 $this->id = "navpoint-" . $playOrder;
741 }
742 $indent = str_repeat("\t", $level) . "\t\t\t\t";
743
744 $nav .= $indent . "<li id=\"" . $this->id . "\"";
745 if (isset($this->writingDirection)) {
746 $nav .= " dir=\"" . $this->writingDirection . "\"";
747 }
748 $nav .= ">\n";
749
750 if (isset($this->contentSrc)) {
751 $nav .= $indent . "\t<a href=\"" . $this->contentSrc . "\">" . $this->label . "</a>\n";
752 } else {
753 $nav .= $indent . "\t<span>" . $this->label . "</span>\n";
754 }
755
756 if (sizeof($this->navPoints) > 0) {
757 $maxLevel++;
758
759 $nav .= $indent . "\t<ol epub:type=\"list\"";
760 if (isset($subLevelClass)) {
761 $nav .= " class=\"" . $subLevelClass . "\"";
762 }
763 if ($subLevelHidden) {
764 $nav .= " hidden=\"hidden\"";
765 }
766 $nav .= ">\n";
767
768 foreach ($this->navPoints as $navPoint) {
769 $retLevel = $navPoint->finalizeEPub3($nav, $playOrder, ($level+2), $subLevelClass, $subLevelHidden);
770 if ($retLevel > $maxLevel) {
771 $maxLevel = $retLevel;
772 }
773 }
774 $nav .= $indent . "\t</ol>\n";
775 }
776
777 $nav .= $indent . "</li>\n";
778
779 return $maxLevel;
780 }
781}
782?> \ No newline at end of file
diff --git a/inc/3rdparty/libraries/PHPePub/EPub.OPF.php b/inc/3rdparty/libraries/PHPePub/EPub.OPF.php
new file mode 100644
index 00000000..803a2108
--- /dev/null
+++ b/inc/3rdparty/libraries/PHPePub/EPub.OPF.php
@@ -0,0 +1,1226 @@
1<?php
2/**
3 * ePub OPF file structure
4 *
5 * @author A. Grandt <php@grandt.com>
6 * @copyright 2009-2014 A. Grandt
7 * @license GNU LGPL, Attribution required for commercial implementations, requested for everything else.
8 * @version 3.20
9 */
10class Opf {
11 const _VERSION = 3.20;
12
13 /* Core Media types.
14 * These types are the only guaranteed mime types any ePub reader must understand.
15 * Any other type muse define a fall back whose fallback chain will end in one of these.
16 */
17 const TYPE_GIF = "image/gif";
18 const TYPE_JPEG = "image/jpeg";
19 const TYPE_PNG = "image/png";
20 const TYPE_SVG = "image/svg+xml";
21 const TYPE_XHTML = "application/xhtml+xml";
22 const TYPE_DTBOOK = "application/x-dtbook+xml";
23 const TYPE_CSS = "text/css";
24 const TYPE_XML = "application/xml";
25 const TYPE_OEB1_DOC = "text/x-oeb1-document"; // Deprecated
26 const TYPE_OEB1_CSS = "text/x-oeb1-css"; // Deprecated
27 const TYPE_NCX = "application/x-dtbncx+xml";
28
29 private $bookVersion = EPub::BOOK_VERSION_EPUB2;
30 private $ident = "BookId";
31
32 public $date = NULL;
33 public $metadata = NULL;
34 public $manifest = NULL;
35 public $spine = NULL;
36 public $guide = NULL;
37
38 /**
39 * Class constructor.
40 *
41 * @return void
42 */
43 function __construct($ident = "BookId", $bookVersion = EPub::BOOK_VERSION_EPUB2) {
44 $this->setIdent($ident);
45 $this->setVersion($bookVersion);
46 $this->metadata = new Metadata();
47 $this->manifest = new Manifest();
48 $this->spine = new Spine();
49 $this->guide = new Guide();
50 }
51
52 /**
53 * Class destructor
54 *
55 * @return void
56 */
57 function __destruct() {
58 unset ($this->bookVersion, $this->ident, $this->date, $this->metadata, $this->manifest, $this->spine, $this->guide);
59 }
60
61 /**
62 *
63 * Enter description here ...
64 *
65 * @param string $ident
66 */
67 function setVersion($bookVersion) {
68 $this->bookVersion = is_string($bookVersion) ? trim($bookVersion) : EPub::BOOK_VERSION_EPUB2;
69 }
70
71 function isEPubVersion2() {
72 return $this->bookVersion === EPub::BOOK_VERSION_EPUB2;
73 }
74
75 /**
76 *
77 * Enter description here ...
78 *
79 * @param string $ident
80 */
81 function setIdent($ident = "BookId") {
82 $this->ident = is_string($ident) ? trim($ident) : "BookId";
83 }
84
85 /**
86 *
87 * Enter description here ...
88 *
89 * @return string
90 */
91 function finalize() {
92 $opf = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
93 . "<package xmlns=\"http://www.idpf.org/2007/opf\" unique-identifier=\"" . $this->ident . "\" version=\"" . $this->bookVersion . "\">\n";
94
95 $opf .= $this->metadata->finalize($this->bookVersion, $this->date);
96 $opf .= $this->manifest->finalize($this->bookVersion);
97 $opf .= $this->spine->finalize();
98
99 if ($this->guide->length() > 0) {
100 $opf .= $this->guide->finalize();
101 }
102
103 return $opf . "</package>\n";
104 }
105
106 // Convenience functions:
107
108 /**
109 *
110 * Enter description here ...
111 *
112 * @param string $title
113 * @param string $language
114 * @param string $identifier
115 * @param string $identifierScheme
116 */
117 function initialize($title, $language, $identifier, $identifierScheme) {
118 $this->metadata->addDublinCore(new DublinCore("title", $title));
119 $this->metadata->addDublinCore(new DublinCore("language", $language));
120
121 $dc = new DublinCore("identifier", $identifier);
122 $dc->addAttr("id", $this->ident);
123 $dc->addOpfAttr("scheme", $identifierScheme);
124 $this->metadata->addDublinCore($dc);
125 }
126
127 /**
128 *
129 * Enter description here ...
130 *
131 * @param string $id
132 * @param string $href
133 * @param string $mediaType
134 */
135 function addItem($id, $href, $mediaType, $properties = NULL) {
136 $this->manifest->addItem(new Item($id, $href, $mediaType, $properties));
137 }
138
139 /**
140 *
141 * Enter description here ...
142 *
143 * @param string $idref
144 * @param bool $linear
145 */
146 function addItemRef($idref, $linear = TRUE) {
147 $this->spine->addItemref(new Itemref($idref, $linear));
148 }
149
150 /**
151 *
152 * Enter description here ...
153 *
154 * @param string $type
155 * @param string $title
156 * @param string $href
157 */
158 function addReference($type, $title, $href) {
159 $this->guide->addReference(new Reference($type, $title, $href));
160 }
161
162 /**
163 *
164 * Enter description here ...
165 *
166 * @param string $name
167 * @param string $value
168 */
169 function addDCMeta($name, $value) {
170 $this->metadata->addDublinCore(new DublinCore($name, $value));
171 }
172
173 /**
174 *
175 * Enter description here ...
176 *
177 * @param string $name
178 * @param string $content
179 */
180 function addMeta($name, $content) {
181 $this->metadata->addMeta($name, $content);
182 }
183
184 /**
185 *
186 * Enter description here ...
187 *
188 * @param string $name
189 * @param string $fileAs
190 * @param string $role Use the MarcCode constants
191 */
192 function addCreator($name, $fileAs = NULL, $role = NULL) {
193 $dc = new DublinCore(DublinCore::CREATOR, trim($name));
194
195 if ($fileAs !== NULL) {
196 $dc->addOpfAttr("file-as", trim($fileAs));
197 }
198
199 if ($role !== NULL) {
200 $dc->addOpfAttr("role", trim($role));
201 }
202
203 $this->metadata->addDublinCore($dc);
204 }
205
206 /**
207 *
208 * Enter description here ...
209 *
210 * @param string $name
211 * @param string $fileAs
212 * @param string $role Use the MarcCode constants
213 */
214 function addColaborator($name, $fileAs = NULL, $role = NULL) {
215 $dc = new DublinCore(DublinCore::CONTRIBUTOR, trim($name));
216
217 if ($fileAs !== NULL) {
218 $dc->addOpfAttr("file-as", trim($fileAs));
219 }
220
221 if ($role !== NULL) {
222 $dc->addOpfAttr("role", trim($role));
223 }
224
225 $this->metadata->addDublinCore($dc);
226 }
227}
228
229/**
230 * ePub OPF Metadata structures
231 */
232class Metadata {
233 const _VERSION = 3.00;
234
235 private $dc = array();
236 private $meta = array();
237
238 /**
239 * Class constructor.
240 *
241 * @return void
242 */
243 function __construct() {
244 }
245
246 /**
247 * Class destructor
248 *
249 * @return void
250 */
251 function __destruct() {
252 unset ($this->dc, $this->meta);
253 }
254
255 /**
256 *
257 * Enter description here ...
258 *
259 * @param DublinCore $dc
260 */
261 function addDublinCore($dc) {
262 if ($dc != NULL && is_object($dc) && get_class($dc) === "DublinCore") {
263 $this->dc[] = $dc;
264 }
265 }
266
267 /**
268 *
269 * Enter description here ...
270 *
271 * @param string $name
272 * @param string $content
273 */
274 function addMeta($name, $content) {
275 $name = is_string($name) ? trim($name) : NULL;
276 if (isset($name)) {
277 $content = is_string($content) ? trim($content) : NULL;
278 }
279 if (isset($content)) {
280 $this->meta[] = array ($name => $content);
281 }
282 }
283
284 /**
285 *
286 * @param string $bookVersion
287 * @param int $date
288 * @return string
289 */
290 function finalize($bookVersion = EPub::BOOK_VERSION_EPUB2, $date = NULL) {
291 $metadata = "\t<metadata xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n";
292 if ($bookVersion === EPub::BOOK_VERSION_EPUB2) {
293 $metadata .= "\t\txmlns:opf=\"http://www.idpf.org/2007/opf\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n";
294 } else {
295 $metadata .= "\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n";
296 if (!isset($date)) {
297 $date = time();
298 }
299 $metadata .= "\t\t<meta property=\"dcterms:modified\">" . gmdate("Y-m-d\TH:i:s\Z", $date) . "</meta>\n";
300 }
301
302 foreach ($this->dc as $dc) {
303 $metadata .= $dc->finalize($bookVersion);
304 }
305
306 foreach ($this->meta as $data) {
307 list($name, $content) = each($data);
308 $metadata .= "\t\t<meta name=\"" . $name . "\" content=\"" . $content . "\" />\n";
309 }
310
311 return $metadata . "\t</metadata>\n";
312 }
313}
314
315/**
316 * ePub OPF Dublin Core (dc:) Metadata structures
317 */
318class DublinCore {
319 const _VERSION = 3.00;
320
321 const CONTRIBUTOR = "contributor";
322 const COVERAGE = "coverage";
323 const CREATOR = "creator";
324 const DATE = "date";
325 const DESCRIPTION = "description";
326 const FORMAT = "format";
327 const IDENTIFIER = "identifier";
328 const LANGUAGE = "language";
329 const PUBLISHER = "publisher";
330 const RELATION = "relation";
331 const RIGHTS = "rights";
332 const SOURCE = "source";
333 const SUBJECT = "subject";
334 const TITLE = "title";
335 const TYPE = "type";
336
337 private $dcName = NULL;
338 private $dcValue = NULL;
339 private $attr = array();
340 private $opfAttr = array();
341
342 /**
343 * Class constructor.
344 *
345 * @return void
346 */
347 function __construct($name, $value) {
348 $this->setDc($name, $value);
349 }
350
351 /**
352 * Class destructor
353 *
354 * @return void
355 */
356 function __destruct() {
357 unset ($this->dcName, $this->dcValue, $this->attr, $this->opfAttr);
358 }
359
360 /**
361 *
362 * Enter description here ...
363 *
364 * @param string $name
365 * @param string $value
366 */
367 function setDc($name, $value) {
368 $this->dcName = is_string($name) ? trim($name) : NULL;
369 if (isset($this->dcName)) {
370 $this->dcValue = isset($value) ? (string)$value : NULL;
371 }
372 if (! isset($this->dcValue)) {
373 $this->dcName = NULL;
374 }
375 }
376
377 /**
378 *
379 * Enter description here ...
380 *
381 * @param string $attrName
382 * @param string $attrValue
383 */
384 function addAttr($attrName, $attrValue) {
385 $attrName = is_string($attrName) ? trim($attrName) : NULL;
386 if (isset($attrName)) {
387 $attrValue = is_string($attrValue) ? trim($attrValue) : NULL;
388 }
389 if (isset($attrValue)) {
390 $this->attr[$attrName] = $attrValue;
391 }
392 }
393
394 /**
395 *
396 * Enter description here ...
397 *
398 * @param string $opfAttrName
399 * @param string $opfAttrValue
400 */
401 function addOpfAttr($opfAttrName, $opfAttrValue) {
402 $opfAttrName = is_string($opfAttrName) ? trim($opfAttrName) : NULL;
403 if (isset($opfAttrName)) {
404 $opfAttrValue = is_string($opfAttrValue) ? trim($opfAttrValue) : NULL;
405 }
406 if (isset($opfAttrValue)) {
407 $this->opfAttr[$opfAttrName] = $opfAttrValue;
408 }
409 }
410
411
412 /**
413 *
414 * @param string $bookVersion
415 * @return string
416 */
417 function finalize($bookVersion = EPub::BOOK_VERSION_EPUB2) {
418 $dc = "\t\t<dc:" . $this->dcName;
419
420 if (sizeof($this->attr) > 0) {
421 while (list($name, $content) = each($this->attr)) {
422 $dc .= " " . $name . "=\"" . $content . "\"";
423 }
424 }
425
426 if ($bookVersion === EPub::BOOK_VERSION_EPUB2 && sizeof($this->opfAttr) > 0) {
427 while (list($name, $content) = each($this->opfAttr)) {
428 $dc .= " opf:" . $name . "=\"" . $content . "\"";
429 }
430 }
431
432 return $dc . ">" . $this->dcValue . "</dc:" . $this->dcName . ">\n";
433 }
434}
435
436/**
437 * ePub OPF Manifest structure
438 */
439class Manifest {
440 const _VERSION = 3.00;
441
442 private $items = array();
443
444 /**
445 * Class constructor.
446 *
447 * @return void
448 */
449 function __construct() {
450 }
451
452 /**
453 * Class destructor
454 *
455 * @return void
456 */
457 function __destruct() {
458 unset ($this->items);
459 }
460
461 /**
462 *
463 * Enter description here ...
464 *
465 * @param Item $item
466 */
467 function addItem($item) {
468 if ($item != NULL && is_object($item) && get_class($item) === "Item") {
469 $this->items[] = $item;
470 }
471 }
472
473 /**
474 *
475 * @param string $bookVersion
476 * @return string
477 */
478 function finalize($bookVersion = EPub::BOOK_VERSION_EPUB2) {
479 $manifest = "\n\t<manifest>\n";
480 foreach ($this->items as $item) {
481 $manifest .= $item->finalize($bookVersion);
482 }
483 return $manifest . "\t</manifest>\n";
484 }
485}
486
487/**
488 * ePub OPF Item structure
489 */
490class Item {
491 const _VERSION = 3.00;
492
493 private $id = NULL;
494 private $href = NULL;
495 private $mediaType = NULL;
496 private $properties = NULL;
497 private $requiredNamespace = NULL;
498 private $requiredModules = NULL;
499 private $fallback = NULL;
500 private $fallbackStyle = NULL;
501
502 /**
503 * Class constructor.
504 *
505 * @return void
506 */
507 function __construct($id, $href, $mediaType, $properties = NULL) {
508 $this->setId($id);
509 $this->setHref($href);
510 $this->setMediaType($mediaType);
511 $this->setProperties($properties);
512 }
513
514 /**
515 * Class destructor
516 *
517 * @return void
518 */
519 function __destruct() {
520 unset ($this->id, $this->href, $this->mediaType);
521 unset ($this->properties, $this->requiredNamespace, $this->requiredModules, $this->fallback, $this->fallbackStyle);
522 }
523
524 /**
525 *
526 * Enter description here ...
527 *
528 * @param string $id
529 */
530 function setId($id) {
531 $this->id = is_string($id) ? trim($id) : NULL;
532 }
533
534 /**
535 *
536 * Enter description here ...
537 *
538 * @param string $href
539 */
540 function setHref($href) {
541 $this->href = is_string($href) ? trim($href) : NULL;
542 }
543
544 /**
545 *
546 * Enter description here ...
547 *
548 * @param string $mediaType
549 */
550 function setMediaType($mediaType) {
551 $this->mediaType = is_string($mediaType) ? trim($mediaType) : NULL;
552 }
553
554 /**
555 *
556 * Enter description here ...
557 *
558 * @param string $properties
559 */
560 function setProperties($properties) {
561 $this->properties = is_string($properties) ? trim($properties) : NULL;
562 }
563
564 /**
565 *
566 * Enter description here ...
567 *
568 * @param string $requiredNamespace
569 */
570 function setRequiredNamespace($requiredNamespace) {
571 $this->requiredNamespace = is_string($requiredNamespace) ? trim($requiredNamespace) : NULL;
572 }
573
574 /**
575 *
576 * Enter description here ...
577 *
578 * @param string $requiredModules
579 */
580 function setRequiredModules($requiredModules) {
581 $this->requiredModules = is_string($requiredModules) ? trim($requiredModules) : NULL;
582 }
583
584 /**
585 *
586 * Enter description here ...
587 *
588 * @param string $fallback
589 */
590 function setfallback($fallback) {
591 $this->fallback = is_string($fallback) ? trim($fallback) : NULL;
592 }
593
594 /**
595 *
596 * Enter description here ...
597 *
598 * @param string $fallbackStyle
599 */
600 function setFallbackStyle($fallbackStyle) {
601 $this->fallbackStyle = is_string($fallbackStyle) ? trim($fallbackStyle) : NULL;
602 }
603
604 /**
605 *
606 * @param string $bookVersion
607 * @return string
608 */
609 function finalize($bookVersion = EPub::BOOK_VERSION_EPUB2) {
610 $item = "\t\t<item id=\"" . $this->id . "\" href=\"" . $this->href . "\" media-type=\"" . $this->mediaType . "\" ";
611 if ($bookVersion === EPub::BOOK_VERSION_EPUB3 && isset($this->properties)) {
612 $item .= "properties=\"" . $this->properties . "\" ";
613 }
614 if (isset($this->requiredNamespace)) {
615 $item .= "\n\t\t\trequired-namespace=\"" . $this->requiredNamespace . "\" ";
616 if (isset($this->requiredModules)) {
617 $item .= "required-modules=\"" . $this->requiredModules . "\" ";
618 }
619 }
620 if (isset($this->fallback)) {
621 $item .= "\n\t\t\tfallback=\"" . $this->fallback . "\" ";
622 }
623 if (isset($this->fallbackStyle)) {
624 $item .= "\n\t\t\tfallback-style=\"" . $this->fallbackStyle . "\" ";
625 }
626 return $item . "/>\n";
627 }
628}
629
630/**
631 * ePub OPF Spine structure
632 */
633class Spine {
634 const _VERSION = 1.00;
635
636 private $itemrefs = array();
637 private $toc = NULL;
638
639 /**
640 * Class constructor.
641 *
642 * @return void
643 */
644 function __construct($toc = "ncx") {
645 $this->setToc($toc);
646 }
647
648 /**
649 * Class destructor
650 *
651 * @return void
652 */
653 function __destruct() {
654 unset ($this->itemrefs, $this->toc);
655 }
656
657 /**
658 *
659 * Enter description here ...
660 *
661 * @param string $toc
662 */
663 function setToc($toc) {
664 $this->toc = is_string($toc) ? trim($toc) : NULL;
665 }
666
667 /**
668 *
669 * Enter description here ...
670 *
671 * @param Itemref $itemref
672 */
673 function addItemref($itemref) {
674 if ($itemref != NULL
675 && is_object($itemref)
676 && get_class($itemref) === "Itemref"
677 && !isset($this->itemrefs[$itemref->getIdref()])) {
678 $this->itemrefs[$itemref->getIdref()] = $itemref;
679 }
680 }
681
682 /**
683 *
684 * Enter description here ...
685 *
686 * @return string
687 */
688 function finalize() {
689 $spine = "\n\t<spine toc=\"" . $this->toc . "\">\n";
690 foreach ($this->itemrefs as $itemref) {
691 $spine .= $itemref->finalize();
692 }
693 return $spine . "\t</spine>\n";
694 }
695}
696
697/**
698 * ePub OPF ItemRef structure
699 */
700class Itemref {
701 const _VERSION = 3.00;
702
703 private $idref = NULL;
704 private $linear = TRUE;
705
706 /**
707 * Class constructor.
708 *
709 * @return void
710 */
711 function __construct($idref, $linear = TRUE) {
712 $this->setIdref($idref);
713 $this->setLinear($linear);
714 }
715
716 /**
717 * Class destructor
718 *
719 * @return void
720 */
721 function __destruct() {
722 unset ($this->idref, $this->linear);
723 }
724
725 /**
726 *
727 * Enter description here ...
728 *
729 * @param string $idref
730 */
731 function setIdref($idref) {
732 $this->idref = is_string($idref) ? trim($idref) : NULL;
733 }
734
735 /**
736 *
737 * Enter description here ...
738 *
739 * @return string $idref
740 */
741 function getIdref() {
742 return $this->idref;
743 }
744
745 /**
746 *
747 * Enter description here ...
748 *
749 * @param bool $linear
750 */
751 function setLinear($linear = TRUE) {
752 $this->linear = $linear === TRUE;
753 }
754
755 /**
756 *
757 * Enter description here ...
758 *
759 * @return string
760 */
761 function finalize() {
762 $itemref = "\t\t<itemref idref=\"" . $this->idref . "\"";
763 if ($this->linear == FALSE) {
764 return $itemref .= " linear=\"no\" />\n";
765 }
766 return $itemref . " />\n";
767 }
768}
769
770/**
771 * ePub OPF Guide structure
772 */
773class Guide {
774 const _VERSION = 3.00;
775
776 private $references = array();
777
778 /**
779 * Class constructor.
780 *
781 * @return void
782 */
783 function __construct() {
784 }
785
786 /**
787 * Class destructor
788 *
789 * @return void
790 */
791 function __destruct() {
792 unset ($this->references);
793 }
794
795 /**
796 *
797 * Enter description here ...
798 *
799 */
800 function length() {
801 return sizeof($this->references);
802 }
803
804 /**
805 *
806 * Enter description here ...
807 *
808 * @param Reference $reference
809 */
810 function addReference($reference) {
811 if ($reference != NULL && is_object($reference) && get_class($reference) === "Reference") {
812 $this->references[] = $reference;
813 }
814 }
815
816 /**
817 *
818 * Enter description here ...
819 *
820 * @return string
821 */
822 function finalize() {
823 $ref = "";
824 if (sizeof($this->references) > 0) {
825 $ref = "\n\t<guide>\n";
826 foreach ($this->references as $reference) {
827 $ref .= $reference->finalize();
828 }
829 $ref .= "\t</guide>\n";
830 }
831 return $ref;
832 }
833}
834
835/**
836 * Reference constants
837 */
838class Reference {
839 const _VERSION = 1.00;
840
841 /* REFERENCE types are derived from the "Chicago Manual of Style"
842 */
843
844 /** Acknowledgements page */
845 const ACKNOWLEDGEMENTS = "acknowledgements";
846
847 /** Bibliography page */
848 const BIBLIOGRAPHY = "bibliography";
849
850 /** Colophon page */
851 const COLOPHON = "colophon";
852
853 /** Copyright page */
854 const COPYRIGHT_PAGE = "copyright-page";
855
856 /** Dedication */
857 const DEDICATION = "dedication";
858
859 /** Epigraph */
860 const EPIGRAPH = "epigraph";
861
862 /** Foreword */
863 const FOREWORD = "foreword";
864
865 /** Glossary page */
866 const GLOSSARY = "glossary";
867
868 /** back-of-book style index */
869 const INDEX = "index";
870
871 /** List of illustrations */
872 const LIST_OF_ILLUSTRATIONS = "loi";
873
874 /** List of tables */
875 const LIST_OF_TABLES = "lot";
876
877 /** Notes page */
878 const NOTES = "notes";
879
880 /** Preface page */
881 const PREFACE = "preface";
882
883 /** Table of contents */
884 const TABLE_OF_CONTENTS = "toc";
885
886 /** Page with possibly title, author, publisher, and other metadata */
887 const TITLE_PAGE = "titlepage";
888
889 /** First page of the book, ie. first page of the first chapter */
890 const TEXT = "text";
891
892 // ******************
893 // ePub3 constants
894 // ******************
895
896 // Document partitions
897 /** The publications cover(s), jacket information, etc. This is officially in ePub3, but works for ePub 2 as well */
898 const COVER = "cover";
899
900 /** Preliminary material to the content body, such as tables of contents, dedications, etc. */
901 const FRONTMATTER = "frontmatter";
902
903 /** The main (body) content of a document. */
904 const BODYMATTER = "bodymatter";
905
906 /** Ancillary material occurring after the document body, such as indices, appendices, etc. */
907 const BACKMATTER = "backmatter";
908
909
910 private $type = NULL;
911 private $title = NULL;
912 private $href = NULL;
913
914 /**
915 * Class constructor.
916 *
917 * @param string $type
918 * @param string $title
919 * @param string $href
920 */
921 function __construct($type, $title, $href) {
922 $this->setType($type);
923 $this->setTitle($title);
924 $this->setHref($href);
925 }
926
927 /**
928 * Class destructor
929 *
930 * @return void
931 */
932 function __destruct() {
933 unset ($this->type, $this->title, $this->href);
934 }
935
936 /**
937 *
938 * Enter description here ...
939 *
940 * @param string $type
941 */
942 function setType($type) {
943 $this->type = is_string($type) ? trim($type) : NULL;
944 }
945
946 /**
947 *
948 * Enter description here ...
949 *
950 * @param string $title
951 */
952 function setTitle($title) {
953 $this->title = is_string($title) ? trim($title) : NULL;
954 }
955
956 /**
957 *
958 * Enter description here ...
959 *
960 * @param string $href
961 */
962 function setHref($href) {
963 $this->href = is_string($href) ? trim($href) : NULL;
964 }
965
966 /**
967 *
968 * Enter description here ...
969 *
970 * @return string
971 */
972 function finalize() {
973 return "\t\t<reference type=\"" . $this->type . "\" title=\"" . $this->title . "\" href=\"" . $this->href . "\" />\n";
974 }
975}
976
977/**
978 * Common Marc codes.
979 * Ref: http://www.loc.gov/marc/relators/
980 */
981class MarcCode {
982 const _VERSION = 3.00;
983
984 /**
985 * Adapter
986 *
987 * Use for a person who
988 * 1) reworks a musical composition, usually for a different medium, or
989 * 2) rewrites novels or stories for motion pictures or other audiovisual medium.
990 */
991 const ADAPTER = "adp";
992
993 /**
994 * Annotator
995 *
996 * Use for a person who writes manuscript annotations on a printed item.
997 */
998 const ANNOTATOR = "ann";
999
1000 /**
1001 * Arranger
1002 *
1003 * Use for a person who transcribes a musical composition, usually for a different
1004 * medium from that of the original; in an arrangement the musical substance remains
1005 * essentially unchanged.
1006 */
1007 const ARRANGER = "arr";
1008
1009 /**
1010 * Artist
1011 *
1012 * Use for a person (e.g., a painter) who conceives, and perhaps also implements,
1013 * an original graphic design or work of art, if specific codes (e.g., [egr],
1014 * [etr]) are not desired. For book illustrators, prefer Illustrator [ill].
1015 */
1016 const ARTIST = "art";
1017
1018 /**
1019 * Associated name
1020 *
1021 * Use as a general relator for a name associated with or found in an item or
1022 * collection, or which cannot be determined to be that of a Former owner [fmo]
1023 * or other designated relator indicative of provenance.
1024 */
1025 const ASSOCIATED_NAME = "asn";
1026
1027 /**
1028 * Author
1029 *
1030 * Use for a person or corporate body chiefly responsible for the intellectual
1031 * or artistic content of a work. This term may also be used when more than one
1032 * person or body bears such responsibility.
1033 */
1034 const AUTHOR = "aut";
1035
1036 /**
1037 * Author in quotations or text extracts
1038 *
1039 * Use for a person whose work is largely quoted or extracted in a works to which
1040 * he or she did not contribute directly. Such quotations are found particularly
1041 * in exhibition catalogs, collections of photographs, etc.
1042 */
1043 const AUTHOR_IN_QUOTES = "aqt";
1044
1045 /**
1046 * Author of afterword, colophon, etc.
1047 *
1048 * Use for a person or corporate body responsible for an afterword, postface,
1049 * colophon, etc. but who is not the chief author of a work.
1050 */
1051 const AUTHOR_OF_AFTERWORD = "aft";
1052
1053 /**
1054 * Author of introduction, etc.
1055 *
1056 * Use for a person or corporate body responsible for an introduction, preface,
1057 * foreword, or other critical matter, but who is not the chief author.
1058 */
1059 const AUTHOR_OF_INTRO = "aui";
1060
1061 /**
1062 * Bibliographic antecedent
1063 *
1064 * Use for the author responsible for a work upon which the work represented by
1065 * the catalog record is based. This can be appropriate for adaptations, sequels,
1066 * continuations, indexes, etc.
1067 */
1068 const BIB_ANTECEDENT = "ant";
1069
1070 /**
1071 * Book producer
1072 *
1073 * Use for the person or firm responsible for the production of books and other
1074 * print media, if specific codes (e.g., [bkd], [egr], [tyd], [prt]) are not desired.
1075 */
1076 const BOOK_PRODUCER = "bkp";
1077
1078 /**
1079 * Collaborator
1080 *
1081 * Use for a person or corporate body that takes a limited part in the elaboration
1082 * of a work of another author or that brings complements (e.g., appendices, notes)
1083 * to the work of another author.
1084 */
1085 const COLABORATOR = "clb";
1086
1087 /**
1088 * Commentator
1089 *
1090 * Use for a person who provides interpretation, analysis, or a discussion of the
1091 * subject matter on a recording, motion picture, or other audiovisual medium.
1092 * Compiler [com] Use for a person who produces a work or publication by selecting
1093 * and putting together material from the works of various persons or bodies.
1094 */
1095 const COMMENTATOR = "cmm";
1096
1097 /**
1098 * Designer
1099 *
1100 * Use for a person or organization responsible for design if specific codes (e.g.,
1101 * [bkd], [tyd]) are not desired.
1102 */
1103 const DESIGNER = "dsr";
1104
1105 /**
1106 * Editor
1107 *
1108 * Use for a person who prepares for publication a work not primarily his/her own,
1109 * such as by elucidating text, adding introductory or other critical matter, or
1110 * technically directing an editorial staff.
1111 */
1112 const EDITORT = "edt";
1113
1114 /**
1115 * Illustrator
1116 *
1117 * Use for the person who conceives, and perhaps also implements, a design or
1118 * illustration, usually to accompany a written text.
1119 */
1120 const ILLUSTRATOR = "ill";
1121
1122 /**
1123 * Lyricist
1124 *
1125 * Use for the writer of the text of a song.
1126 */
1127 const LYRICIST = "lyr";
1128
1129 /**
1130 * Metadata contact
1131 *
1132 * Use for the person or organization primarily responsible for compiling and
1133 * maintaining the original description of a metadata set (e.g., geospatial
1134 * metadata set).
1135 */
1136 const METADATA_CONTACT = "mdc";
1137
1138 /**
1139 * Musician
1140 *
1141 * Use for the person who performs music or contributes to the musical content
1142 * of a work when it is not possible or desirable to identify the function more
1143 * precisely.
1144 */
1145 const MUSICIAN = "mus";
1146
1147 /**
1148 * Narrator
1149 *
1150 * Use for the speaker who relates the particulars of an act, occurrence, or
1151 * course of events.
1152 */
1153 const NARRATOR = "nrt";
1154
1155 /**
1156 * Other
1157 *
1158 * Use for relator codes from other lists which have no equivalent in the MARC
1159 * list or for terms which have not been assigned a code.
1160 */
1161 const OTHER = "oth";
1162
1163 /**
1164 * Photographer
1165 *
1166 * Use for the person or organization responsible for taking photographs, whether
1167 * they are used in their original form or as reproductions.
1168 */
1169 const PHOTOGRAPHER = "pht";
1170
1171 /**
1172 * Printer
1173 *
1174 * Use for the person or organization who prints texts, whether from type or plates.
1175 */
1176 const PRINTER = "prt";
1177
1178 /**
1179 * Redactor
1180 *
1181 * Use for a person who writes or develops the framework for an item without
1182 * being intellectually responsible for its content.
1183 */
1184 const REDACTOR = "red";
1185
1186 /**
1187 * Reviewer
1188 *
1189 * Use for a person or corporate body responsible for the review of book, motion
1190 * picture, performance, etc.
1191 */
1192 const REVIEWER = "rev";
1193
1194 /**
1195 * Sponsor
1196 *
1197 * Use for the person or agency that issued a contract, or under whose auspices
1198 * a work has been written, printed, published, etc.
1199 */
1200 const SPONSOR = "spn";
1201
1202 /**
1203 * Thesis advisor
1204 *
1205 * Use for the person under whose supervision a degree candidate develops and
1206 * presents a thesis, memoir, or text of a dissertation.
1207 */
1208 const THESIS_ADVISOR = "ths";
1209
1210 /**
1211 * Transcriber
1212 *
1213 * Use for a person who prepares a handwritten or typewritten copy from original
1214 * material, including from dictated or orally recorded material.
1215 */
1216 const TRANSCRIBER = "trc";
1217
1218 /**
1219 * Translator
1220 *
1221 * Use for a person who renders a text from one language into another, or from
1222 * an older form of a language into the modern form.
1223 */
1224 const TRANSLATOR = "trl";
1225}
1226?>
diff --git a/inc/3rdparty/libraries/PHPePub/EPub.php b/inc/3rdparty/libraries/PHPePub/EPub.php
new file mode 100644
index 00000000..836c0512
--- /dev/null
+++ b/inc/3rdparty/libraries/PHPePub/EPub.php
@@ -0,0 +1,2429 @@
1<?php
2/**
3 * Create an ePub compatible book file.
4 *
5 * Please note, once finalized a book can no longer have chapters of data added or changed.
6 *
7 * License: GNU LGPL, Attribution required for commercial implementations, requested for everything else.
8 *
9 * Thanks to: Adam Schmalhofer and Kirstyn Fox for invaluable input and for "nudging" me in the right direction :)
10 *
11 * @author A. Grandt <php@grandt.com>
12 * @copyright 2009-2014 A. Grandt
13 * @license GNU LGPL 2.1
14 * @version 3.20
15 * @link http://www.phpclasses.org/package/6115
16 * @link https://github.com/Grandt/PHPePub
17 * @uses Zip.php version 1.50; http://www.phpclasses.org/browse/package/6110.html or https://github.com/Grandt/PHPZip
18 */
19class EPub {
20 const VERSION = 3.20;
21 const REQ_ZIP_VERSION = 1.60;
22
23 const IDENTIFIER_UUID = 'UUID';
24 const IDENTIFIER_URI = 'URI';
25 const IDENTIFIER_ISBN = 'ISBN';
26
27 /** Ignore all external references, and do not process the file for these */
28 const EXTERNAL_REF_IGNORE = 0;
29 /** Process the file for external references and add them to the book */
30 const EXTERNAL_REF_ADD = 1;
31 /** Process the file for external references and add them to the book, but remove images, and img tags */
32 const EXTERNAL_REF_REMOVE_IMAGES = 2;
33 /** Process the file for external references and add them to the book, but replace images, and img tags with [image] */
34 const EXTERNAL_REF_REPLACE_IMAGES = 3;
35
36 const DIRECTION_LEFT_TO_RIGHT = "ltr";
37 const DIRECTION_RIGHT_TO_LEFT = "rtl";
38
39 const BOOK_VERSION_EPUB2 = "2.0";
40 const BOOK_VERSION_EPUB3 = "3.0";
41
42 private $bookVersion = EPub::BOOK_VERSION_EPUB2;
43
44 public $maxImageWidth = 768;
45 public $maxImageHeight = 1024;
46
47 public $splitDefaultSize = 250000;
48 /** Gifs can crash some early ADE based readers, and are disabled by default.
49 * getImage will convert these if it can, unless this is set to TRUE.
50 */
51 public $isGifImagesEnabled = FALSE;
52 public $isReferencesAddedToToc = TRUE;
53
54 private $zip;
55
56 private $title = "";
57 private $language = "en";
58 private $identifier = "";
59 private $identifierType = "";
60 private $description = "";
61 private $author = "";
62 private $authorSortKey = "";
63 private $publisherName = "";
64 private $publisherURL = "";
65 private $date = 0;
66 private $rights = "";
67 private $coverage = "";
68 private $relation = "";
69 private $sourceURL = "";
70
71 private $chapterCount = 0;
72 private $opf = NULL;
73 private $ncx = NULL;
74 private $isFinalized = FALSE;
75 private $isCoverImageSet = FALSE;
76 private $buildTOC = FALSE;
77 private $tocTitle = NULL;
78 private $tocFileName = NULL;
79 private $tocCSSClass = NULL;
80 private $tocAddReferences = FALSE;
81 private $tocCssFileName = NULL;
82
83 private $fileList = array();
84 private $writingDirection = EPub::DIRECTION_LEFT_TO_RIGHT;
85 private $languageCode = "en";
86
87 /**
88 * Used for building the TOC.
89 * If this list is overwritten it MUST contain at least "text" as an element.
90 */
91 public $referencesOrder = NULL;
92
93 private $dateformat = 'Y-m-d\TH:i:s.000000P'; // ISO 8601 long
94 private $dateformatShort = 'Y-m-d'; // short date format to placate ePubChecker.
95 private $headerDateFormat = "D, d M Y H:i:s T";
96
97 protected $isCurlInstalled;
98 protected $isGdInstalled;
99 protected $isExifInstalled;
100 protected $isFileGetContentsInstalled;
101 protected $isFileGetContentsExtInstalled;
102
103 private $bookRoot = "OEBPS/";
104 private $docRoot = NULL;
105 private $EPubMark = TRUE;
106 private $generator = "";
107
108 private $log = NULL;
109 public $isLogging = TRUE;
110
111 public $encodeHTML = FALSE;
112
113 private $mimetypes = array(
114 "js" => "application/x-javascript", "swf" => "application/x-shockwave-flash", "xht" => "application/xhtml+xml", "xhtml" => "application/xhtml+xml", "zip" => "application/zip",
115 "aif" => "audio/x-aiff", "aifc" => "audio/x-aiff", "aiff" => "audio/x-aiff", "au" => "audio/basic", "kar" => "audio/midi", "m3u" => "audio/x-mpegurl", "mid" => "audio/midi", "midi" => "audio/midi", "mp2" => "audio/mpeg", "mp3" => "audio/mpeg", "mpga" => "audio/mpeg", "oga" => "audio/ogg", "ogg" => "audio/ogg", "ra" => "audio/x-realaudio", "ram" => "audio/x-pn-realaudio", "rm" => "audio/x-pn-realaudio", "rpm" => "audio/x-pn-realaudio-plugin", "snd" => "audio/basic", "wav" => "audio/x-wav",
116 "bmp" => "image/bmp", "djv" => "image/vnd.djvu", "djvu" => "image/vnd.djvu", "gif" => "image/gif", "ief" => "image/ief", "jpe" => "image/jpeg", "jpeg" => "image/jpeg", "jpg" => "image/jpeg", "pbm" => "image/x-portable-bitmap", "pgm" => "image/x-portable-graymap", "png" => "image/png", "pnm" => "image/x-portable-anymap", "ppm" => "image/x-portable-pixmap", "ras" => "image/x-cmu-raster", "rgb" => "image/x-rgb", "tif" => "image/tif", "tiff" => "image/tiff", "wbmp" => "image/vnd.wap.wbmp", "xbm" => "image/x-xbitmap", "xpm" => "image/x-xpixmap", "xwd" => "image/x-windowdump",
117 "asc" => "text/plain", "css" => "text/css", "etx" => "text/x-setext", "htm" => "text/html", "html" => "text/html", "rtf" => "text/rtf", "rtx" => "text/richtext", "sgm" => "text/sgml", "sgml" => "text/sgml", "tsv" => "text/tab-seperated-values", "txt" => "text/plain", "wml" => "text/vnd.wap.wml", "wmls" => "text/vnd.wap.wmlscript", "xml" => "text/xml", "xsl" => "text/xml",
118 "avi" => "video/x-msvideo", "mov" => "video/quicktime", "movie" => "video/x-sgi-movie", "mp4" => "video/mp4", "mpe" => "video/mpeg", "mpeg" => "video/mpeg", "mpg" => "video/mpeg", "mxu" => "video/vnd.mpegurl", "ogv" => "video/ogg", "qt" => "video/quicktime", "webm" => "video/webm");
119
120 // These are the ONLY allowed types in that these are the ones ANY reader must support, any other MUST have the fallback attribute pointing to one of these.
121 private $coreMediaTypes = array("image/gif", "image/jpeg", "image/png", "image/svg+xml", "application/xhtml+xml", "application/x-dtbook+xml", "application/xml", "application/x-dtbncx+xml", "text/css", "text/x-oeb1-css", "text/x-oeb1-document");
122
123 private $opsContentTypes = array("application/xhtml+xml", "application/x-dtbook+xml", "application/xml", "application/x-dtbncx+xml", "text/x-oeb1-document");
124
125 private $forbiddenCharacters = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%");
126
127 private $htmlContentHeader = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n<title></title>\n</head>\n<body>\n";
128 private $htmlContentFooter = "</body>\n</html>\n";
129
130 /**
131 * Class constructor.
132 *
133 * @return void
134 */
135 function __construct($bookVersion = EPub::BOOK_VERSION_EPUB2, $languageCode = "en", $writingDirection = EPub::DIRECTION_LEFT_TO_RIGHT) {
136 include_once("Zip.php");
137 include_once("Logger.php");
138
139 $this->bookVersion = $bookVersion;
140 $this->writingDirection = $writingDirection;
141 $this->languageCode = $languageCode;
142
143 $this->log = new Logger("EPub", $this->isLogging);
144
145 /* Prepare Logging. Just in case it's used. later */
146 if ($this->isLogging) {
147 $this->log->logLine("EPub class version....: " . self::VERSION);
148 $this->log->logLine("EPub req. Zip version.: " . self::REQ_ZIP_VERSION);
149 $this->log->logLine("Zip version...........: " . Zip::VERSION);
150 $this->log->dumpInstalledModules();
151 }
152
153 if (!defined("Zip::VERSION") || Zip::VERSION < self::REQ_ZIP_VERSION) {
154 die("<p>EPub version " . self::VERSION . " requires Zip.php at version " . self::REQ_ZIP_VERSION . " or higher.<br />You can obtain the latest version from <a href=\"http://www.phpclasses.org/browse/package/6110.html\">http://www.phpclasses.org/browse/package/6110.html</a>.</p>");
155 }
156
157 include_once("EPubChapterSplitter.php");
158 include_once("EPub.HtmlEntities.php");
159 include_once("EPub.NCX.php");
160 include_once("EPub.OPF.php");
161
162 $this->initialize();
163 }
164
165 /**
166 * Class destructor
167 *
168 * @return void
169 * @TODO make sure elements in the destructor match the current class elements
170 */
171 function __destruct() {
172 unset($this->bookVersion, $this->maxImageWidth, $this->maxImageHeight);
173 unset($this->splitDefaultSize, $this->isGifImagesEnabled, $this->isReferencesAddedToToc);
174 unset($this->zip, $this->title, $this->language, $this->identifier, $this->identifierType);
175 unset($this->description, $this->author, $this->authorSortKey, $this->publisherName);
176 unset($this->publisherURL, $this->date, $this->rights, $this->coverage, $this->relation);
177 unset($this->sourceURL, $this->chapterCount, $this->opf, $this->ncx, $this->isFinalized);
178 unset($this->isCoverImageSet, $this->fileList, $this->writingDirection, $this->languageCode);
179 unset($this->referencesOrder, $this->dateformat, $this->dateformatShort, $this->headerDateFormat);
180 unset($this->isCurlInstalled, $this->isGdInstalled, $this->isExifInstalled);
181 unset($this->isFileGetContentsInstalled, $this->isFileGetContentsExtInstalled, $this->bookRoot);
182 unset($this->docRoot, $this->EPubMark, $this->generator, $this->log, $this->isLogging);
183 unset($this->encodeHTML, $this->mimetypes, $this->coreMediaTypes, $this->opsContentTypes);
184 unset($this->forbiddenCharacters, $this->htmlContentHeader, $this->htmlContentFooter);
185 unset($this->buildTOC, $this->tocTitle, $this->tocCSSClass, $this->tocAddReferences);
186 unset($this->tocFileName, $this->tocCssFileName);
187 }
188
189 /**
190 * initialize defaults.
191 */
192 private function initialize() {
193 $this->referencesOrder = array(
194 Reference::COVER => "Cover Page",
195 Reference::TITLE_PAGE => "Title Page",
196 Reference::ACKNOWLEDGEMENTS => "Acknowledgements",
197 Reference::BIBLIOGRAPHY => "Bibliography",
198 Reference::COLOPHON => "Colophon",
199 Reference::COPYRIGHT_PAGE => "Copyright",
200 Reference::DEDICATION => "Dedication",
201 Reference::EPIGRAPH => "Epigraph",
202 Reference::FOREWORD => "Foreword",
203 Reference::TABLE_OF_CONTENTS => "Table of Contents",
204 Reference::NOTES => "Notes",
205 Reference::PREFACE => "Preface",
206 Reference::TEXT => "First Page",
207 Reference::LIST_OF_ILLUSTRATIONS => "List of Illustrations",
208 Reference::LIST_OF_TABLES => "List of Tables",
209 Reference::GLOSSARY => "Glossary",
210 Reference::INDEX => "Index");
211
212 $this->docRoot = filter_input(INPUT_SERVER, "DOCUMENT_ROOT") . "/";
213
214 $this->isCurlInstalled = extension_loaded('curl') && function_exists('curl_version');
215 $this->isGdInstalled = extension_loaded('gd') && function_exists('gd_info');
216 $this->isExifInstalled = extension_loaded('exif') && function_exists('exif_imagetype');
217 $this->isFileGetContentsInstalled = function_exists('file_get_contents');
218 $this->isFileGetContentsExtInstalled = $this->isFileGetContentsInstalled && ini_get('allow_url_fopen');
219
220 $this->zip = new Zip();
221 $this->zip->setExtraField(FALSE);
222 $this->zip->addFile("application/epub+zip", "mimetype");
223 $this->zip->setExtraField(TRUE);
224 $this->zip->addDirectory("META-INF");
225
226 $this->content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<container version=\"1.0\" xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\">\n\t<rootfiles>\n\t\t<rootfile full-path=\"" . $this->bookRoot . "book.opf\" media-type=\"application/oebps-package+xml\" />\n\t</rootfiles>\n</container>\n";
227
228 if (!$this->isEPubVersion2()) {
229 $this->htmlContentHeader = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
230 . "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n"
231 . "<head>"
232 . "<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n"
233 . "<title></title>\n"
234 . "</head>\n"
235 . "<body>\n";
236 }
237
238 $this->zip->addFile($this->content, "META-INF/container.xml", 0, NULL, FALSE);
239 $this->content = NULL;
240 $this->ncx = new Ncx(NULL, NULL, NULL, $this->languageCode, $this->writingDirection);
241 $this->opf = new Opf();
242 $this->ncx->setVersion($this->bookVersion);
243 $this->opf->setVersion($this->bookVersion);
244 $this->opf->addItem("ncx", "book.ncx", Ncx::MIMETYPE);
245 $this->chapterCount = 0;
246 }
247
248 /**
249 * Add dynamically generated data as a file to the book.
250 *
251 * @param string $fileName Filename to use for the file, must be unique for the book.
252 * @param string $fileId Unique identifier for the file.
253 * @param string $fileData File data
254 * @param string $mimetype file mime type
255 * @return bool $success
256 */
257 function addFile($fileName, $fileId, $fileData, $mimetype) {
258 if ($this->isFinalized || array_key_exists($fileName, $this->fileList)) {
259 return FALSE;
260 }
261
262 $fileName = $this->normalizeFileName($fileName);
263
264 $compress = (strpos($mimetype, "image/") !== 0);
265
266 $this->zip->addFile($fileData, $this->bookRoot.$fileName, 0, NULL, $compress);
267 $this->fileList[$fileName] = $fileName;
268 $this->opf->addItem($fileId, $fileName, $mimetype);
269 return TRUE;
270 }
271
272 /**
273 * Add a large file directly from the filestystem to the book.
274 *
275 * @param string $fileName Filename to use for the file, must be unique for the book.
276 * @param string $fileId Unique identifier for the file.
277 * @param string $filePath File path
278 * @param string $mimetype file mime type
279 * @return bool $success
280 */
281 function addLargeFile($fileName, $fileId, $filePath, $mimetype) {
282 if ($this->isFinalized || array_key_exists($fileName, $this->fileList)) {
283 return FALSE;
284 }
285 $fileName = $this->normalizeFileName($fileName);
286
287 if ($this->zip->addLargeFile($filePath, $this->bookRoot.$fileName)) {
288 $this->fileList[$fileName] = $fileName;
289 $this->opf->addItem($fileId, $fileName, $mimetype);
290 return TRUE;
291 }
292 return FALSE;
293 }
294
295 /**
296 * Add a CSS file to the book.
297 *
298 * @param string $fileName Filename to use for the CSS file, must be unique for the book.
299 * @param string $fileId Unique identifier for the file.
300 * @param string $fileData CSS data
301 * @param int $externalReferences How to handle external references, EPub::EXTERNAL_REF_IGNORE, EPub::EXTERNAL_REF_ADD or EPub::EXTERNAL_REF_REMOVE_IMAGES? See documentation for <code>processCSSExternalReferences</code> for explanation. Default is EPub::EXTERNAL_REF_IGNORE.
302 * @param string $baseDir Default is "", meaning it is pointing to the document root. NOT used if $externalReferences is set to EPub::EXTERNAL_REF_IGNORE.
303 *
304 * @return bool $success
305 */
306 function addCSSFile($fileName, $fileId, $fileData, $externalReferences = EPub::EXTERNAL_REF_IGNORE, $baseDir = "") {
307 if ($this->isFinalized || array_key_exists($fileName, $this->fileList)) {
308 return FALSE;
309 }
310 $fileName = Zip::getRelativePath($fileName);
311 $fileName = preg_replace('#^[/\.]+#i', "", $fileName);
312
313 if ($externalReferences !== EPub::EXTERNAL_REF_IGNORE) {
314 $cssDir = pathinfo($fileName);
315 $cssDir = preg_replace('#^[/\.]+#i', "", $cssDir["dirname"] . "/");
316 if (!empty($cssDir)) {
317 $cssDir = preg_replace('#[^/]+/#i', "../", $cssDir);
318 }
319
320 $this->processCSSExternalReferences($fileData, $externalReferences, $baseDir, $cssDir);
321 }
322
323 $this->addFile($fileName, "css_" . $fileId, $fileData, "text/css");
324
325 return TRUE;
326 }
327
328 /**
329 * Add a chapter to the book, as a chapter should not exceed 250kB, you can parse an array with multiple parts as $chapterData.
330 * These will still only show up as a single chapter in the book TOC.
331 *
332 * @param string $chapterName Name of the chapter, will be use din the TOC
333 * @param string $fileName Filename to use for the chapter, must be unique for the book.
334 * @param string $chapter Chapter text in XHTML or array $chapterData valid XHTML data for the chapter. File should NOT exceed 250kB.
335 * @param bool $autoSplit Should the chapter be split if it exceeds the default split size? Default=FALSE, only used if $chapterData is a string.
336 * @param int $externalReferences How to handle external references, EPub::EXTERNAL_REF_IGNORE, EPub::EXTERNAL_REF_ADD or EPub::EXTERNAL_REF_REMOVE_IMAGES? See documentation for <code>processChapterExternalReferences</code> for explanation. Default is EPub::EXTERNAL_REF_IGNORE.
337 * @param string $baseDir Default is "", meaning it is pointing to the document root. NOT used if $externalReferences is set to EPub::EXTERNAL_REF_IGNORE.
338 * @return mixed $success FALSE if the addition failed, else the new NavPoint.
339 */
340 function addChapter($chapterName, $fileName, $chapterData = NULL, $autoSplit = FALSE, $externalReferences = EPub::EXTERNAL_REF_IGNORE, $baseDir = "") {
341 if ($this->isFinalized) {
342 return FALSE;
343 }
344 $fileName = Zip::getRelativePath($fileName);
345 $fileName = preg_replace('#^[/\.]+#i', "", $fileName);
346
347 $chapter = $chapterData;
348 if ($autoSplit && is_string($chapterData) && mb_strlen($chapterData) > $this->splitDefaultSize) {
349 $splitter = new EPubChapterSplitter();
350
351 $chapterArray = $splitter->splitChapter($chapterData);
352 if (count($chapterArray) > 1) {
353 $chapter = $chapterArray;
354 }
355 }
356
357 if (!empty($chapter) && is_string($chapter)) {
358 if ($externalReferences !== EPub::EXTERNAL_REF_IGNORE) {
359 $htmlDirInfo = pathinfo($fileName);
360 $htmlDir = preg_replace('#^[/\.]+#i', "", $htmlDirInfo["dirname"] . "/");
361 $this->processChapterExternalReferences($chapter, $externalReferences, $baseDir, $htmlDir);
362 }
363
364 if ($this->encodeHTML === TRUE) {
365 $chapter = $this->encodeHtml($chapter);
366 }
367
368 $this->chapterCount++;
369 $this->addFile($fileName, "chapter" . $this->chapterCount, $chapter, "application/xhtml+xml");
370 $this->opf->addItemRef("chapter" . $this->chapterCount);
371
372 $navPoint = new NavPoint($this->decodeHtmlEntities($chapterName), $fileName, "chapter" . $this->chapterCount);
373 $this->ncx->addNavPoint($navPoint);
374 $this->ncx->chapterList[$chapterName] = $navPoint;
375 } else if (is_array($chapter)) {
376 $fileNameParts = pathinfo($fileName);
377 $extension = $fileNameParts['extension'];
378 $name = $fileNameParts['filename'];
379
380 $partCount = 0;
381 $this->chapterCount++;
382
383 $oneChapter = each($chapter);
384 while ($oneChapter) {
385 list($k, $v) = $oneChapter;
386 if ($this->encodeHTML === TRUE) {
387 $v = $this->encodeHtml($v);
388 }
389
390 if ($externalReferences !== EPub::EXTERNAL_REF_IGNORE) {
391 $this->processChapterExternalReferences($v, $externalReferences, $baseDir);
392 }
393 $partCount++;
394 $partName = $name . "_" . $partCount;
395 $this->addFile($partName . "." . $extension, $partName, $v, "application/xhtml+xml");
396 $this->opf->addItemRef($partName);
397
398 $oneChapter = each($chapter);
399 }
400 $partName = $name . "_1." . $extension;
401 $navPoint = new NavPoint($this->decodeHtmlEntities($chapterName), $partName, $partName);
402 $this->ncx->addNavPoint($navPoint);
403
404 $this->ncx->chapterList[$chapterName] = $navPoint;
405 } else if (!isset($chapterData) && strpos($fileName, "#") > 0) {
406 $this->chapterCount++;
407 //$this->opf->addItemRef("chapter" . $this->chapterCount);
408
409 $navPoint = new NavPoint($this->decodeHtmlEntities($chapterName), $fileName, "chapter" . $this->chapterCount);
410 $this->ncx->addNavPoint($navPoint);
411 $this->ncx->chapterList[$chapterName] = $navPoint;
412 } else if (!isset($chapterData) && $fileName=="TOC.xhtml") {
413 $this->chapterCount++;
414 $this->opf->addItemRef("toc");
415
416 $navPoint = new NavPoint($this->decodeHtmlEntities($chapterName), $fileName, "chapter" . $this->chapterCount);
417 $this->ncx->addNavPoint($navPoint);
418 $this->ncx->chapterList[$chapterName] = $navPoint;
419 }
420 return $navPoint;
421 }
422
423 /**
424 * Add one chapter level.
425 *
426 * Subsequent chapters will be added to this level.
427 *
428 * @param string $navTitle
429 * @param string $navId
430 * @param string $navClass
431 * @param int $isNavHidden
432 * @param string $writingDirection
433 * @return NavPoint The new NavPoint for that level.
434 */
435 function subLevel($navTitle = NULL, $navId = NULL, $navClass = NULL, $isNavHidden = FALSE, $writingDirection = NULL) {
436 return $this->ncx->subLevel($this->decodeHtmlEntities($navTitle), $navId, $navClass, $isNavHidden, $writingDirection);
437 }
438
439 /**
440 * Step back one chapter level.
441 *
442 * Subsequent chapters will be added to this chapters parent level.
443 */
444 function backLevel() {
445 $this->ncx->backLevel();
446 }
447
448 /**
449 * Step back to the root level.
450 *
451 * Subsequent chapters will be added to the rooot NavMap.
452 */
453 function rootLevel() {
454 $this->ncx->rootLevel();
455 }
456
457 /**
458 * Step back to the given level.
459 * Useful for returning to a previous level from deep within the structure.
460 * Values below 2 will have the same effect as rootLevel()
461 *
462 * @param int $newLevel
463 */
464 function setCurrentLevel($newLevel) {
465 $this->ncx->setCurrentLevel($newLevel);
466 }
467
468 /**
469 * Get current level count.
470 * The indentation of the current structure point.
471 *
472 * @return current level count;
473 */
474 function getCurrentLevel() {
475 return $this->ncx->getCurrentLevel();
476 }
477
478 /**
479 * Wrap ChapterContent with Head and Footer
480 *
481 * @param $content
482 * @return string $content
483 */
484 private function wrapChapter($content) {
485 return $this->htmlContentHeader . "\n" . $content . "\n" . $this->htmlContentFooter;
486 }
487
488 /**
489 * Reference pages is usually one or two pages for items such as Table of Contents, reference lists, Author notes or Acknowledgements.
490 * These do not show up in the regular navigation list.
491 *
492 * As they are supposed to be short.
493 *
494 * @param string $pageName Name of the chapter, will be use din the TOC
495 * @param string $fileName Filename to use for the chapter, must be unique for the book.
496 * @param string $pageData Page content in XHTML. File should NOT exceed 250kB.
497 * @param string $reference Reference key
498 * @param int $externalReferences How to handle external references. See documentation for <code>processChapterExternalReferences</code> for explanation. Default is EPub::EXTERNAL_REF_IGNORE.
499 * @param string $baseDir Default is "", meaning it is pointing to the document root. NOT used if $externalReferences is set to EPub::EXTERNAL_REF_IGNORE.
500 * @return bool $success
501 */
502 function addReferencePage($pageName, $fileName, $pageData, $reference, $externalReferences = EPub::EXTERNAL_REF_IGNORE, $baseDir = "") {
503 if ($this->isFinalized) {
504 return FALSE;
505 }
506 $fileName = Zip::getRelativePath($fileName);
507 $fileName = preg_replace('#^[/\.]+#i', "", $fileName);
508
509
510 if (!empty($pageData) && is_string($pageData)) {
511 if ($this->encodeHTML === TRUE) {
512 $pageData = $this->encodeHtml($pageData);
513 }
514
515 $this->wrapChapter($pageData);
516
517 if ($externalReferences !== EPub::EXTERNAL_REF_IGNORE) {
518 $htmlDirInfo = pathinfo($fileName);
519 $htmlDir = preg_replace('#^[/\.]+#i', "", $htmlDirInfo["dirname"] . "/");
520 $this->processChapterExternalReferences($pageData, $externalReferences, $baseDir, $htmlDir);
521 }
522
523 $this->addFile($fileName, "ref_" . $reference, $pageData, "application/xhtml+xml");
524
525 if ($reference !== Reference::TABLE_OF_CONTENTS || !isset($this->ncx->referencesList[$reference])) {
526 $this->opf->addItemRef("ref_" . $reference, FALSE);
527 $this->opf->addReference($reference, $pageName, $fileName);
528
529 $this->ncx->referencesList[$reference] = $fileName;
530 $this->ncx->referencesName[$reference] = $pageName;
531 }
532 return TRUE;
533 }
534 return TRUE;
535 }
536
537 /**
538 * Add custom metadata to the book.
539 *
540 * It is up to the builder to make sure there are no collisions. Metadata are just key value pairs.
541 *
542 * @param string $name
543 * @param string $content
544 */
545 function addCustomMetadata($name, $content) {
546 $this->opf->addMeta($name, $content);
547 }
548
549 /**
550 * Add DublinCore metadata to the book
551 *
552 * Use the DublinCore constants included in EPub, ie DublinCore::DATE
553 *
554 * @param string $dublinCore name
555 * @param string $value
556 */
557 function addDublinCoreMetadata($dublinCoreConstant, $value) {
558 if ($this->isFinalized) {
559 return;
560 }
561
562 $this->opf->addDCMeta($dublinCoreConstant, $this->decodeHtmlEntities($value));
563 }
564
565 /**
566 * Add a cover image to the book.
567 * If the $imageData is not set, the function assumes the $fileName is the path to the image file.
568 *
569 * The styling and structure of the generated XHTML is heavily inspired by the XHTML generated by Calibre.
570 *
571 * @param string $fileName Filename to use for the image, must be unique for the book.
572 * @param string $imageData Binary image data
573 * @param string $mimetype Image mimetype, such as "image/jpeg" or "image/png".
574 * @return bool $success
575 */
576 function setCoverImage($fileName, $imageData = NULL, $mimetype = NULL) {
577 if ($this->isFinalized || $this->isCoverImageSet || array_key_exists("CoverPage.html", $this->fileList)) {
578 return FALSE;
579 }
580
581 if ($imageData == NULL) {
582 // assume $fileName is the valid file path.
583 if (!file_exists($fileName)) {
584 // Attempt to locate the file using the doc root.
585 $rp = realpath($this->docRoot . "/" . $fileName);
586
587 if ($rp !== FALSE) {
588 // only assign the docroot path if it actually exists there.
589 $fileName = $rp;
590 }
591 }
592 $image = $this->getImage($fileName);
593 $imageData = $image['image'];
594 $mimetype = $image['mime'];
595 $fileName = preg_replace("#\.[^\.]+$#", "." . $image['ext'], $fileName);
596 }
597
598
599 $path = pathinfo($fileName);
600 $imgPath = "images/" . $path["basename"];
601
602 if (empty($mimetype) && file_exists($fileName)) {
603 list($width, $height, $type, $attr) = getimagesize($fileName);
604 $mimetype = image_type_to_mime_type($type);
605 }
606 if (empty($mimetype)) {
607 $ext = strtolower($path['extension']);
608 if ($ext == "jpg") {
609 $ext = "jpeg";
610 }
611 $mimetype = "image/" . $ext;
612 }
613
614 $coverPage = "";
615
616 if ($this->isEPubVersion2()) {
617 $coverPage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
618 . "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n"
619 . " \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
620 . "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\" xml:lang=\"en\">\n"
621 . "\t<head>\n"
622 . "\t\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n"
623 . "\t\t<title>Cover Image</title>\n"
624 . "\t\t<link type=\"text/css\" rel=\"stylesheet\" href=\"Styles/CoverPage.css\" />\n"
625 . "\t</head>\n"
626 . "\t<body>\n"
627 . "\t\t<div>\n"
628 . "\t\t\t<img src=\"" . $imgPath . "\" alt=\"Cover image\" style=\"height: 100%\"/>\n"
629 . "\t\t</div>\n"
630 . "\t</body>\n"
631 . "</html>\n";
632 } else {
633 $coverPage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
634 . "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n"
635 . "<head>"
636 . "\t<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n"
637 . "\t\t<title>Cover Image</title>\n"
638 . "\t\t<link type=\"text/css\" rel=\"stylesheet\" href=\"Styles/CoverPage.css\" />\n"
639 . "\t</head>\n"
640 . "\t<body>\n"
641 . "\t\t<section epub:type=\"cover\">\n"
642 . "\t\t\t<img src=\"" . $imgPath . "\" alt=\"Cover image\" style=\"height: 100%\"/>\n"
643 . "\t\t</section>\n"
644 . "\t</body>\n"
645 . "</html>\n";
646 }
647 $coverPageCss = "@page, body, div, img {\n"
648 . "\tpadding: 0pt;\n"
649 . "\tmargin:0pt;\n"
650 . "}\n\nbody {\n"
651 . "\ttext-align: center;\n"
652 . "}\n";
653
654 $this->addCSSFile("Styles/CoverPage.css", "CoverPageCss", $coverPageCss);
655 $this->addFile($imgPath, "CoverImage", $imageData, $mimetype);
656 $this->addReferencePage("CoverPage", "CoverPage.xhtml", $coverPage, "cover");
657 $this->isCoverImageSet = TRUE;
658 return TRUE;
659 }
660
661 /**
662 * Process external references from a HTML to the book. The chapter itself is not stored.
663 * the HTML is scanned for &lt;link..., &lt;style..., and &lt;img tags.
664 * Embedded CSS styles and links will also be processed.
665 * Script tags are not processed, as scripting should be avoided in e-books.
666 *
667 * EPub keeps track of added files, and duplicate files referenced across multiple
668 * chapters, are only added once.
669 *
670 * If the $doc is a string, it is assumed to be the content of an HTML file,
671 * else is it assumes to be a DOMDocument.
672 *
673 * Basedir is the root dir the HTML is supposed to "live" in, used to resolve
674 * relative references such as <code>&lt;img src="../images/image.png"/&gt;</code>
675 *
676 * $externalReferences determines how the function will handle external references.
677 *
678 * @param mixed &$doc (referenced)
679 * @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.
680 * @param string $baseDir Default is "", meaning it is pointing to the document root.
681 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
682 *
683 * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
684 */
685 protected function processChapterExternalReferences(&$doc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "") {
686 if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
687 return FALSE;
688 }
689
690 $backPath = preg_replace('#[^/]+/#i', "../", $htmlDir);
691 $isDocAString = is_string($doc);
692 $xmlDoc = NULL;
693
694 if ($isDocAString) {
695 $xmlDoc = new DOMDocument();
696 @$xmlDoc->loadHTML($doc);
697 } else {
698 $xmlDoc = $doc;
699 }
700
701 $this->processChapterStyles($xmlDoc, $externalReferences, $baseDir, $htmlDir);
702 $this->processChapterLinks($xmlDoc, $externalReferences, $baseDir, $htmlDir, $backPath);
703 $this->processChapterImages($xmlDoc, $externalReferences, $baseDir, $htmlDir, $backPath);
704 $this->processChapterSources($xmlDoc, $externalReferences, $baseDir, $htmlDir, $backPath);
705
706 if ($isDocAString) {
707 //$html = $xmlDoc->saveXML();
708
709 $htmlNode = $xmlDoc->getElementsByTagName("html");
710 $headNode = $xmlDoc->getElementsByTagName("head");
711 $bodyNode = $xmlDoc->getElementsByTagName("body");
712
713 $htmlNS = "";
714 for ($index = 0; $index < $htmlNode->item(0)->attributes->length; $index++) {
715 $nodeName = $htmlNode->item(0)->attributes->item($index)->nodeName;
716 $nodeValue = $htmlNode->item(0)->attributes->item($index)->nodeValue;
717
718 if ($nodeName != "xmlns") {
719 $htmlNS .= " $nodeName=\"$nodeValue\"";
720 }
721 }
722
723 $xml = new DOMDocument('1.0', "utf-8");
724 $xml->lookupPrefix("http://www.w3.org/1999/xhtml");
725 $xml->preserveWhiteSpace = FALSE;
726 $xml->formatOutput = TRUE;
727
728 $xml2Doc = new DOMDocument('1.0', "utf-8");
729 $xml2Doc->lookupPrefix("http://www.w3.org/1999/xhtml");
730 $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");
731 $html = $xml2Doc->getElementsByTagName("html")->item(0);
732 $html->appendChild($xml2Doc->importNode($headNode->item(0), TRUE));
733 $html->appendChild($xml2Doc->importNode($bodyNode->item(0), TRUE));
734
735 // force pretty printing and correct formatting, should not be needed, but it is.
736 $xml->loadXML($xml2Doc->saveXML());
737 $doc = $xml->saveXML();
738
739 if (!$this->isEPubVersion2()) {
740 $doc = preg_replace('#^\s*<!DOCTYPE\ .+?>\s*#im', '', $doc);
741 }
742 }
743 return TRUE;
744 }
745
746 /**
747 * Process images referenced from an CSS file to the book.
748 *
749 * $externalReferences determins how the function will handle external references.
750 *
751 * @param string &$cssFile (referenced)
752 * @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.
753 * @param string $baseDir Default is "", meaning it is pointing to the document root.
754 * @param string $cssDir The of the CSS file's directory from the root of the archive.
755 *
756 * @return bool FALSE if unsuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
757 */
758 protected function processCSSExternalReferences(&$cssFile, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $cssDir = "") {
759 if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
760 return FALSE;
761 }
762
763 $backPath = preg_replace('#[^/]+/#i', "../", $cssDir);
764 $imgs = null;
765 preg_match_all('#url\s*\([\'\"\s]*(.+?)[\'\"\s]*\)#im', $cssFile, $imgs, PREG_SET_ORDER);
766
767 $itemCount = count($imgs);
768 for ($idx = 0; $idx < $itemCount; $idx++) {
769 $img = $imgs[$idx];
770 if ($externalReferences === EPub::EXTERNAL_REF_REMOVE_IMAGES || $externalReferences === EPub::EXTERNAL_REF_REPLACE_IMAGES) {
771 $cssFile = str_replace($img[0], "", $cssFile);
772 } else {
773 $source = $img[1];
774
775 $pathData = pathinfo($source);
776 $internalSrc = $pathData['basename'];
777 $internalPath = "";
778 $isSourceExternal = FALSE;
779
780 if ($this->resolveImage($source, $internalPath, $internalSrc, $isSourceExternal, $baseDir, $cssDir, $backPath)) {
781 $cssFile = str_replace($img[0], "url('" . $backPath . $internalPath . "')", $cssFile);
782 } else if ($isSourceExternal) {
783 $cssFile = str_replace($img[0], "", $cssFile); // External image is missing
784 } // else do nothing, if the image is local, and missing, assume it's been generated.
785 }
786 }
787 return TRUE;
788 }
789
790 /**
791 * Process style tags in a DOMDocument. Styles will be passed as CSS files and reinserted into the document.
792 *
793 * @param DOMDocument &$xmlDoc (referenced)
794 * @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.
795 * @param string $baseDir Default is "", meaning it is pointing to the document root.
796 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
797 *
798 * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
799 */
800 protected function processChapterStyles(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "") {
801 if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
802 return FALSE;
803 }
804 // process inlined CSS styles in style tags.
805 $styles = $xmlDoc->getElementsByTagName("style");
806 $styleCount = $styles->length;
807 for ($styleIdx = 0; $styleIdx < $styleCount; $styleIdx++) {
808 $style = $styles->item($styleIdx);
809
810 $styleData = preg_replace('#[/\*\s]*\<\!\[CDATA\[[\s\*/]*#im', "", $style->nodeValue);
811 $styleData = preg_replace('#[/\*\s]*\]\]\>[\s\*/]*#im', "", $styleData);
812
813 $this->processCSSExternalReferences($styleData, $externalReferences, $baseDir, $htmlDir);
814 $style->nodeValue = "\n" . trim($styleData) . "\n";
815 }
816 return TRUE;
817 }
818
819 /**
820 * 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.
821 * Link types text/css will be passed as CSS files.
822 *
823 * @param DOMDocument &$xmlDoc (referenced)
824 * @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.
825 * @param string $baseDir Default is "", meaning it is pointing to the document root.
826 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
827 * @param string $backPath The path to get back to the root of the archive from $htmlDir.
828 *
829 * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
830 */
831 protected function processChapterLinks(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "", $backPath = "") {
832 if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
833 return FALSE;
834 }
835 // process link tags.
836 $links = $xmlDoc->getElementsByTagName("link");
837 $linkCount = $links->length;
838 for ($linkIdx = 0; $linkIdx < $linkCount; $linkIdx++) {
839 $link = $links->item($linkIdx);
840 $source = $link->attributes->getNamedItem("href")->nodeValue;
841 $sourceData = NULL;
842
843 $pathData = pathinfo($source);
844 $internalSrc = $pathData['basename'];
845
846 if (preg_match('#^(http|ftp)s?://#i', $source) == 1) {
847 $urlinfo = parse_url($source);
848
849 if (strpos($urlinfo['path'], $baseDir."/") !== FALSE) {
850 $internalSrc = substr($urlinfo['path'], strpos($urlinfo['path'], $baseDir."/") + strlen($baseDir) + 1);
851 }
852
853 @$sourceData = getFileContents($source);
854 } else if (strpos($source, "/") === 0) {
855 @$sourceData = file_get_contents($this->docRoot . $source);
856 } else {
857 @$sourceData = file_get_contents($this->docRoot . $baseDir . "/" . $source);
858 }
859
860 if (!empty($sourceData)) {
861 if (!array_key_exists($internalSrc, $this->fileList)) {
862 $mime = $link->attributes->getNamedItem("type")->nodeValue;
863 if (empty($mime)) {
864 $mime = "text/plain";
865 }
866 if ($mime == "text/css") {
867 $this->processCSSExternalReferences($sourceData, $externalReferences, $baseDir, $htmlDir);
868 $this->addCSSFile($internalSrc, $internalSrc, $sourceData, EPub::EXTERNAL_REF_IGNORE, $baseDir);
869 $link->setAttribute("href", $backPath . $internalSrc);
870 } else {
871 $this->addFile($internalSrc, $internalSrc, $sourceData, $mime);
872 }
873 $this->fileList[$internalSrc] = $source;
874 } else {
875 $link->setAttribute("href", $backPath . $internalSrc);
876 }
877 } // else do nothing, if the link is local, and missing, assume it's been generated.
878 }
879 return TRUE;
880 }
881
882 /**
883 * Process img tags in a DOMDocument.
884 * $externalReferences will determine what will happen to these images, and the img src will be rewritten accordingly.
885 *
886 * @param DOMDocument &$xmlDoc (referenced)
887 * @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.
888 * @param string $baseDir Default is "", meaning it is pointing to the document root.
889 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
890 * @param string $backPath The path to get back to the root of the archive from $htmlDir.
891 *
892 * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
893 */
894 protected function processChapterImages(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "", $backPath = "") {
895 if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
896 return FALSE;
897 }
898 // process img tags.
899 $postProcDomElememts = array();
900 $images = $xmlDoc->getElementsByTagName("img");
901 $itemCount = $images->length;
902
903 for ($idx = 0; $idx < $itemCount; $idx++) {
904 $img = $images->item($idx);
905
906 if ($externalReferences === EPub::EXTERNAL_REF_REMOVE_IMAGES) {
907 $postProcDomElememts[] = $img;
908 } else if ($externalReferences === EPub::EXTERNAL_REF_REPLACE_IMAGES) {
909 $altNode = $img->attributes->getNamedItem("alt");
910 $alt = "image";
911 if ($altNode !== NULL && strlen($altNode->nodeValue) > 0) {
912 $alt = $altNode->nodeValue;
913 }
914 $postProcDomElememts[] = array($img, $this->createDomFragment($xmlDoc, "<em>[" . $alt . "]</em>"));
915 } else {
916 $source = $img->attributes->getNamedItem("src")->nodeValue;
917
918 $parsedSource = parse_url($source);
919 $internalSrc = $this->sanitizeFileName(urldecode(pathinfo($parsedSource['path'], PATHINFO_BASENAME)));
920 $internalPath = "";
921 $isSourceExternal = FALSE;
922
923 if ($this->resolveImage($source, $internalPath, $internalSrc, $isSourceExternal, $baseDir, $htmlDir, $backPath)) {
924 $img->setAttribute("src", $backPath . $internalPath);
925 } else if ($isSourceExternal) {
926 $postProcDomElememts[] = $img; // External image is missing
927 } // else do nothing, if the image is local, and missing, assume it's been generated.
928 }
929 }
930
931 foreach ($postProcDomElememts as $target) {
932 if (is_array($target)) {
933 $target[0]->parentNode->replaceChild($target[1], $target[0]);
934 } else {
935 $target->parentNode->removeChild($target);
936 }
937 }
938 return TRUE;
939 }
940
941 /**
942 * Process source tags in a DOMDocument.
943 * $externalReferences will determine what will happen to these images, and the img src will be rewritten accordingly.
944 *
945 * @param DOMDocument &$xmlDoc (referenced)
946 * @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.
947 * @param string $baseDir Default is "", meaning it is pointing to the document root.
948 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
949 * @param string $backPath The path to get back to the root of the archive from $htmlDir.
950 *
951 * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE).
952 */
953 protected function processChapterSources(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "", $backPath = "") {
954 if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) {
955 return FALSE;
956 }
957
958 if ($this->bookVersion !== EPub::BOOK_VERSION_EPUB3) {
959 // ePub 2 does not support multimedia formats, and they must be removed.
960 $externalReferences = EPub::EXTERNAL_REF_REMOVE_IMAGES;
961 }
962
963 $postProcDomElememts = array();
964 $images = $xmlDoc->getElementsByTagName("source");
965 $itemCount = $images->length;
966 for ($idx = 0; $idx < $itemCount; $idx++) {
967 $img = $images->item($idx);
968 if ($externalReferences === EPub::EXTERNAL_REF_REMOVE_IMAGES) {
969 $postProcDomElememts[] = $img;
970 } else if ($externalReferences === EPub::EXTERNAL_REF_REPLACE_IMAGES) {
971 $altNode = $img->attributes->getNamedItem("alt");
972 $alt = "image";
973 if ($altNode !== NULL && strlen($altNode->nodeValue) > 0) {
974 $alt = $altNode->nodeValue;
975 }
976 $postProcDomElememts[] = array($img, $this->createDomFragment($xmlDoc, "[" . $alt . "]"));
977 } else {
978 $source = $img->attributes->getNamedItem("src")->nodeValue;
979
980 $parsedSource = parse_url($source);
981 $internalSrc = $this->sanitizeFileName(urldecode(pathinfo($parsedSource['path'], PATHINFO_BASENAME)));
982 $internalPath = "";
983 $isSourceExternal = FALSE;
984
985 if ($this->resolveMedia($source, $internalPath, $internalSrc, $isSourceExternal, $baseDir, $htmlDir, $backPath)) {
986 $img->setAttribute("src", $backPath . $internalPath);
987 } else if ($isSourceExternal) {
988 $postProcDomElememts[] = $img; // External image is missing
989 } // else do nothing, if the image is local, and missing, assume it's been generated.
990 }
991 }
992 }
993
994 /**
995 * Resolve an image src and determine it's target location and add it to the book.
996 *
997 * @param string $source Image Source link.
998 * @param string &$internalPath (referenced) Return value, will be set to the target path and name in the book.
999 * @param string &$internalSrc (referenced) Return value, will be set to the target name in the book.
1000 * @param string &$isSourceExternal (referenced) Return value, will be set to TRUE if the image originated from a full URL.
1001 * @param string $baseDir Default is "", meaning it is pointing to the document root.
1002 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
1003 * @param string $backPath The path to get back to the root of the archive from $htmlDir.
1004 */
1005 protected function resolveImage($source, &$internalPath, &$internalSrc, &$isSourceExternal, $baseDir = "", $htmlDir = "", $backPath = "") {
1006 if ($this->isFinalized) {
1007 return FALSE;
1008 }
1009 $imageData = NULL;
1010
1011 if (preg_match('#^(http|ftp)s?://#i', $source) == 1) {
1012 $urlinfo = parse_url($source);
1013 $urlPath = pathinfo($urlinfo['path']);
1014
1015 if (strpos($urlinfo['path'], $baseDir."/") !== FALSE) {
1016 $internalSrc = $this->sanitizeFileName(urldecode(substr($urlinfo['path'], strpos($urlinfo['path'], $baseDir."/") + strlen($baseDir) + 1)));
1017 }
1018 $internalPath = $urlinfo["scheme"] . "/" . $urlinfo["host"] . "/" . pathinfo($urlinfo["path"], PATHINFO_DIRNAME);
1019 $isSourceExternal = TRUE;
1020 $imageData = $this->getImage($source);
1021 } else if (strpos($source, "/") === 0) {
1022 $internalPath = pathinfo($source, PATHINFO_DIRNAME);
1023
1024 $path = $source;
1025 if (!file_exists($path)) {
1026 $path = $this->docRoot . $path;
1027 }
1028
1029 $imageData = $this->getImage($path);
1030 } else {
1031 $internalPath = $htmlDir . "/" . preg_replace('#^[/\.]+#', '', pathinfo($source, PATHINFO_DIRNAME));
1032
1033 $path = $baseDir . "/" . $source;
1034 if (!file_exists($path)) {
1035 $path = $this->docRoot . $path;
1036 }
1037
1038 $imageData = $this->getImage($path);
1039 }
1040 if ($imageData !== FALSE) {
1041 $iSrcInfo = pathinfo($internalSrc);
1042 if (!empty($imageData['ext']) && $imageData['ext'] != $iSrcInfo['extension']) {
1043 $internalSrc = $iSrcInfo['filename'] . "." . $imageData['ext'];
1044 }
1045 $internalPath = Zip::getRelativePath("images/" . $internalPath . "/" . $internalSrc);
1046 if (!array_key_exists($internalPath, $this->fileList)) {
1047 $this->addFile($internalPath, "i_" . $internalSrc, $imageData['image'], $imageData['mime']);
1048 $this->fileList[$internalPath] = $source;
1049 }
1050 return TRUE;
1051 }
1052 return FALSE;
1053 }
1054
1055 /**
1056 * Resolve a media src and determine it's target location and add it to the book.
1057 *
1058 * @param string $source Source link.
1059 * @param string $internalPath (referenced) Return value, will be set to the target path and name in the book.
1060 * @param string $internalSrc (referenced) Return value, will be set to the target name in the book.
1061 * @param string $isSourceExternal (referenced) Return value, will be set to TRUE if the image originated from a full URL.
1062 * @param string $baseDir Default is "", meaning it is pointing to the document root.
1063 * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive.
1064 * @param string $backPath The path to get back to the root of the archive from $htmlDir.
1065 */
1066 protected function resolveMedia($source, &$internalPath, &$internalSrc, &$isSourceExternal, $baseDir = "", $htmlDir = "", $backPath = "") {
1067 if ($this->isFinalized) {
1068 return FALSE;
1069 }
1070 $mediaPath = NULL;
1071 $tmpFile;
1072
1073 if (preg_match('#^(http|ftp)s?://#i', $source) == 1) {
1074 $urlinfo = parse_url($source);
1075
1076 if (strpos($urlinfo['path'], $baseDir."/") !== FALSE) {
1077 $internalSrc = substr($urlinfo['path'], strpos($urlinfo['path'], $baseDir."/") + strlen($baseDir) + 1);
1078 }
1079 $internalPath = $urlinfo["scheme"] . "/" . $urlinfo["host"] . "/" . pathinfo($urlinfo["path"], PATHINFO_DIRNAME);
1080 $isSourceExternal = TRUE;
1081 $mediaPath = $this->getFileContents($source, true);
1082 $tmpFile = $mediaPath;
1083 } else if (strpos($source, "/") === 0) {
1084 $internalPath = pathinfo($source, PATHINFO_DIRNAME);
1085
1086 $mediaPath = $source;
1087 if (!file_exists($mediaPath)) {
1088 $mediaPath = $this->docRoot . $mediaPath;
1089 }
1090 } else {
1091 $internalPath = $htmlDir . "/" . preg_replace('#^[/\.]+#', '', pathinfo($source, PATHINFO_DIRNAME));
1092
1093 $mediaPath = $baseDir . "/" . $source;
1094 if (!file_exists($mediaPath)) {
1095 $mediaPath = $this->docRoot . $mediaPath;
1096 }
1097 }
1098
1099 if ($mediaPath !== FALSE) {
1100 $mime = $this->getMime($source);
1101 $internalPath = Zip::getRelativePath("media/" . $internalPath . "/" . $internalSrc);
1102
1103 if (!array_key_exists($internalPath, $this->fileList) &&
1104 $this->addLargeFile($internalPath, "m_" . $internalSrc, $mediaPath, $mime)) {
1105 $this->fileList[$internalPath] = $source;
1106 }
1107 if (isset($tmpFile)) {
1108 unlink($tmpFile);
1109 }
1110 return TRUE;
1111 }
1112 return FALSE;
1113 }
1114
1115 /**
1116 * Get Book Chapter count.
1117 *
1118 * @access public
1119 * @return number of chapters
1120 */
1121 function getChapterCount() {
1122 return $this->chapterCount;
1123 }
1124
1125 /**
1126 * Book title, mandatory.
1127 *
1128 * Used for the dc:title metadata parameter in the OPF file as well as the DocTitle attribute in the NCX file.
1129 *
1130 * @param string $title
1131 * @access public
1132 * @return bool $success
1133 */
1134 function setTitle($title) {
1135 if ($this->isFinalized) {
1136 return FALSE;
1137 }
1138 $this->title = $title;
1139 return TRUE;
1140 }
1141
1142 /**
1143 * Get Book title.
1144 *
1145 * @access public
1146 * @return $title
1147 */
1148 function getTitle() {
1149 return $this->title;
1150 }
1151
1152 /**
1153 * Book language, mandatory
1154 *
1155 * Use the RFC3066 Language codes, such as "en", "da", "fr" etc.
1156 * Defaults to "en".
1157 *
1158 * Used for the dc:language metadata parameter in the OPF file.
1159 *
1160 * @param string $language
1161 * @access public
1162 * @return bool $success
1163 */
1164 function setLanguage($language) {
1165 if ($this->isFinalized || mb_strlen($language) != 2) {
1166 return FALSE;
1167 }
1168 $this->language = $language;
1169 return TRUE;
1170 }
1171
1172 /**
1173 * Get Book language.
1174 *
1175 * @access public
1176 * @return $language
1177 */
1178 function getLanguage() {
1179 return $this->language;
1180 }
1181
1182 /**
1183 * Unique book identifier, mandatory.
1184 * Use the URI, or ISBN if available.
1185 *
1186 * An unambiguous reference to the resource within a given context.
1187 *
1188 * Recommended best practice is to identify the resource by means of a
1189 * string conforming to a formal identification system.
1190 *
1191 * Used for the dc:identifier metadata parameter in the OPF file, as well
1192 * as dtb:uid in the NCX file.
1193 *
1194 * Identifier type should only be:
1195 * EPub::IDENTIFIER_URI
1196 * EPub::IDENTIFIER_ISBN
1197 * EPub::IDENTIFIER_UUID
1198 *
1199 * @param string $identifier
1200 * @param string $identifierType
1201 * @access public
1202 * @return bool $success
1203 */
1204 function setIdentifier($identifier, $identifierType) {
1205 if ($this->isFinalized || ($identifierType !== EPub::IDENTIFIER_URI && $identifierType !== EPub::IDENTIFIER_ISBN && $identifierType !== EPub::IDENTIFIER_UUID)) {
1206 return FALSE;
1207 }
1208 $this->identifier = $identifier;
1209 $this->identifierType = $identifierType;
1210 return TRUE;
1211 }
1212
1213 /**
1214 * Get Book identifier.
1215 *
1216 * @access public
1217 * @return $identifier
1218 */
1219 function getIdentifier() {
1220 return $this->identifier;
1221 }
1222
1223 /**
1224 * Get Book identifierType.
1225 *
1226 * @access public
1227 * @return $identifierType
1228 */
1229 function getIdentifierType() {
1230 return $this->identifierType;
1231 }
1232
1233 /**
1234 * Book description, optional.
1235 *
1236 * An account of the resource.
1237 *
1238 * Description may include but is not limited to: an abstract, a table of
1239 * contents, a graphical representation, or a free-text account of the
1240 * resource.
1241 *
1242 * Used for the dc:source metadata parameter in the OPF file
1243 *
1244 * @param string $description
1245 * @access public
1246 * @return bool $success
1247 */
1248 function setDescription($description) {
1249 if ($this->isFinalized) {
1250 return FALSE;
1251 }
1252 $this->description = $description;
1253 return TRUE;
1254 }
1255
1256 /**
1257 * Get Book description.
1258 *
1259 * @access public
1260 * @return $description
1261 */
1262 function getDescription() {
1263 return $this->description;
1264 }
1265
1266 /**
1267 * Book author or creator, optional.
1268 * The $authorSortKey is basically how the name is to be sorted, usually
1269 * it's "Lastname, First names" where the $author is the straight
1270 * "Firstnames Lastname"
1271 *
1272 * An entity primarily responsible for making the resource.
1273 *
1274 * Examples of a Creator include a person, an organization, or a service.
1275 * Typically, the name of a Creator should be used to indicate the entity.
1276 *
1277 * Used for the dc:creator metadata parameter in the OPF file and the
1278 * docAuthor attribure in the NCX file.
1279 * The sort key is used for the opf:file-as attribute in dc:creator.
1280 *
1281 * @param string $author
1282 * @param string $authorSortKey
1283 * @access public
1284 * @return bool $success
1285 */
1286 function setAuthor($author, $authorSortKey) {
1287 if ($this->isFinalized) {
1288 return FALSE;
1289 }
1290 $this->author = $author;
1291 $this->authorSortKey = $authorSortKey;
1292 return TRUE;
1293 }
1294
1295 /**
1296 * Get Book author.
1297 *
1298 * @access public
1299 * @return $author
1300 */
1301 function getAuthor() {
1302 return $this->author;
1303 }
1304
1305 /**
1306 * Publisher Information, optional.
1307 *
1308 * An entity responsible for making the resource available.
1309 *
1310 * Examples of a Publisher include a person, an organization, or a service.
1311 * Typically, the name of a Publisher should be used to indicate the entity.
1312 *
1313 * Used for the dc:publisher and dc:relation metadata parameters in the OPF file.
1314 *
1315 * @param string $publisherName
1316 * @param string $publisherURL
1317 * @access public
1318 * @return bool $success
1319 */
1320 function setPublisher($publisherName, $publisherURL) {
1321 if ($this->isFinalized) {
1322 return FALSE;
1323 }
1324 $this->publisherName = $publisherName;
1325 $this->publisherURL = $publisherURL;
1326 return TRUE;
1327 }
1328
1329 /**
1330 * Get Book publisherName.
1331 *
1332 * @access public
1333 * @return $publisherName
1334 */
1335 function getPublisherName() {
1336 return $this->publisherName;
1337 }
1338
1339 /**
1340 * Get Book publisherURL.
1341 *
1342 * @access public
1343 * @return $publisherURL
1344 */
1345 function getPublisherURL() {
1346 return $this->publisherURL;
1347 }
1348
1349 /**
1350 * Release date, optional. If left blank, the time of the finalization will
1351 * be used.
1352 *
1353 * A point or period of time associated with an event in the lifecycle of
1354 * the resource.
1355 *
1356 * Date may be used to express temporal information at any level of
1357 * granularity. Recommended best practice is to use an encoding scheme,
1358 * such as the W3CDTF profile of ISO 8601 [W3CDTF].
1359 *
1360 * Used for the dc:date metadata parameter in the OPF file
1361 *
1362 * @param long $timestamp
1363 * @access public
1364 * @return bool $success
1365 */
1366 function setDate($timestamp) {
1367 if ($this->isFinalized) {
1368 return FALSE;
1369 }
1370 $this->date = $timestamp;
1371 $this->opf->date = $timestamp;
1372 return TRUE;
1373 }
1374
1375 /**
1376 * Get Book date.
1377 *
1378 * @access public
1379 * @return $date
1380 */
1381 function getDate() {
1382 return $this->date;
1383 }
1384
1385 /**
1386 * Book (copy)rights, optional.
1387 *
1388 * Information about rights held in and over the resource.
1389 *
1390 * Typically, rights information includes a statement about various
1391 * property rights associated with the resource, including intellectual
1392 * property rights.
1393 *
1394 * Used for the dc:rights metadata parameter in the OPF file
1395 *
1396 * @param string $rightsText
1397 * @access public
1398 * @return bool $success
1399 */
1400 function setRights($rightsText) {
1401 if ($this->isFinalized) {
1402 return FALSE;
1403 }
1404 $this->rights = $rightsText;
1405 return TRUE;
1406 }
1407
1408 /**
1409 * Get Book rights.
1410 *
1411 * @access public
1412 * @return $rights
1413 */
1414 function getRights() {
1415 return $this->rights;
1416 }
1417
1418 /**
1419 * Add book Subject.
1420 *
1421 * The topic of the resource.
1422 *
1423 * Typically, the subject will be represented using keywords, key phrases,
1424 * or classification codes. Recommended best practice is to use a
1425 * controlled vocabulary. To describe the spatial or temporal topic of the
1426 * resource, use the Coverage element.
1427 *
1428 * @param string $subject
1429 */
1430 function setSubject($subject) {
1431 if ($this->isFinalized) {
1432 return;
1433 }
1434 $this->opf->addDCMeta(DublinCore::SUBJECT, $this->decodeHtmlEntities($subject));
1435 }
1436
1437 /**
1438 * Book source URL, optional.
1439 *
1440 * A related resource from which the described resource is derived.
1441 *
1442 * The described resource may be derived from the related resource in whole
1443 * or in part. Recommended best practice is to identify the related
1444 * resource by means of a string conforming to a formal identification system.
1445 *
1446 * Used for the dc:source metadata parameter in the OPF file
1447 *
1448 * @param string $sourceURL
1449 * @access public
1450 * @return bool $success
1451 */
1452 function setSourceURL($sourceURL) {
1453 if ($this->isFinalized) {
1454 return FALSE;
1455 }
1456 $this->sourceURL = $sourceURL;
1457 return TRUE;
1458 }
1459
1460 /**
1461 * Get Book sourceURL.
1462 *
1463 * @access public
1464 * @return $sourceURL
1465 */
1466 function getSourceURL() {
1467 return $this->sourceURL;
1468 }
1469
1470 /**
1471 * Coverage, optional.
1472 *
1473 * The spatial or temporal topic of the resource, the spatial applicability
1474 * of the resource, or the jurisdiction under which the resource is relevant.
1475 *
1476 * Spatial topic and spatial applicability may be a named place or a location
1477 * specified by its geographic coordinates. Temporal topic may be a named
1478 * period, date, or date range. A jurisdiction may be a named administrative
1479 * entity or a geographic place to which the resource applies. Recommended
1480 * best practice is to use a controlled vocabulary such as the Thesaurus of
1481 * Geographic Names [TGN]. Where appropriate, named places or time periods
1482 * can be used in preference to numeric identifiers such as sets of
1483 * coordinates or date ranges.
1484 *
1485 * Used for the dc:coverage metadata parameter in the OPF file
1486 *
1487 * Same as ->addDublinCoreMetadata(DublinCore::COVERAGE, $coverage);
1488 *
1489 * @param string $coverage
1490 * @access public
1491 * @return bool $success
1492 */
1493 function setCoverage($coverage) {
1494 if ($this->isFinalized) {
1495 return FALSE;
1496 }
1497 $this->coverage = $coverage;
1498 return TRUE;
1499 }
1500
1501 /**
1502 * Get Book coverage.
1503 *
1504 * @access public
1505 * @return $coverage
1506 */
1507 function getCoverage() {
1508 return $this->coverage;
1509 }
1510
1511 /**
1512 * Set book Relation.
1513 *
1514 * A related resource.
1515 *
1516 * Recommended best practice is to identify the related resource by means
1517 * of a string conforming to a formal identification system.
1518 *
1519 * @param string $relation
1520 */
1521 function setRelation($relation) {
1522 if ($this->isFinalized) {
1523 return;
1524 }
1525 $this->relation = $relation;
1526 }
1527
1528 /**
1529 * Get the book relation.
1530 *
1531 * @return string The relation.
1532 */
1533 function getRelation() {
1534 return $this->relation;
1535 }
1536
1537 /**
1538 * Set book Generator.
1539 *
1540 * The generator is a meta tag added to the ncx file, it is not visible
1541 * from within the book, but is a kind of electronic watermark.
1542 *
1543 * @param string $generator
1544 */
1545 function setGenerator($generator) {
1546 if ($this->isFinalized) {
1547 return;
1548 }
1549 $this->generator = $generator;
1550 }
1551
1552 /**
1553 * Get the book relation.
1554 *
1555 * @return string The generator identity string.
1556 */
1557 function getGenerator() {
1558 return $this->generator;
1559 }
1560
1561 /**
1562 * Set ePub date formate to the short yyyy-mm-dd form, for compliance with
1563 * a bug in EpubCheck, prior to its version 1.1.
1564 *
1565 * The latest version of ePubCheck can be obtained here:
1566 * http://code.google.com/p/epubcheck/
1567 *
1568 * @access public
1569 * @return bool $success
1570 */
1571 function setShortDateFormat() {
1572 if ($this->isFinalized) {
1573 return FALSE;
1574 }
1575 $this->dateformat = $this->dateformatShort;
1576 return TRUE;
1577 }
1578
1579 /**
1580 * @Deprecated
1581 */
1582 function setIgnoreEmptyBuffer($ignoreEmptyBuffer = TRUE) {
1583 die ("Function was deprecated, functionality is no longer needed.");
1584 }
1585
1586 /**
1587 * Set the references title for the ePub 3 landmarks section
1588 *
1589 * @param string $referencesTitle
1590 * @param string $referencesId
1591 * @param string $referencesClass
1592 * @return bool
1593 */
1594 function setReferencesTitle($referencesTitle = "Guide", $referencesId = "", $referencesClass = "references") {
1595 if ($this->isFinalized) {
1596 return FALSE;
1597 }
1598 $this->ncx->referencesTitle = is_string($referencesTitle) ? trim($referencesTitle) : "Guide";
1599 $this->ncx->referencesId = is_string($referencesId) ? trim($referencesId) : "references";
1600 $this->ncx->referencesClass = is_string($referencesClass) ? trim($referencesClass) : "references";
1601 return TRUE;
1602 }
1603
1604 /**
1605 * Set the references title for the ePub 3 landmarks section
1606 *
1607 * @param bool $referencesTitle
1608 */
1609 function setisReferencesAddedToToc($isReferencesAddedToToc = TRUE) {
1610 if ($this->isFinalized) {
1611 return FALSE;
1612 }
1613 $this->isReferencesAddedToToc = $isReferencesAddedToToc === TRUE;
1614 return TRUE;
1615 }
1616
1617 /**
1618 * Get Book status.
1619 *
1620 * @access public
1621 * @return bool
1622 */
1623 function isFinalized() {
1624 return $this->isFinalized;
1625 }
1626
1627 /**
1628 * Build the Table of Contents. This is not strictly necessary, as most eReaders will build it from the navigation structure in the .ncx file.
1629 *
1630 * @param string $cssFileName Include a link to this css file in the TOC html.
1631 * @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".
1632 * @param string $title Title of the Table of contents. Default is "Table of Contents". Use this for ie. languages other than English.
1633 * @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.
1634 * @param bool $addToIndex Add the TOC to the NCX index at the current leve/position. Default is FALSE
1635 * @param string $tocFileName Change teh default name of the TOC file. The default is "TOC.xhtml"
1636 */
1637 function buildTOC($cssFileName = NULL, $tocCSSClass = "toc", $title = "Table of Contents", $addReferences = TRUE, $addToIndex = FALSE, $tocFileName = "TOC.xhtml") {
1638 if ($this->isFinalized) {
1639 return FALSE;
1640 }
1641 $this->buildTOC = TRUE;
1642 $this->tocTitle = $title;
1643 $this->tocFileName = $this->normalizeFileName($tocFileName);
1644 if (!empty($cssFileName)) {
1645 $this->tocCSSFileName = $this->normalizeFileName($cssFileName);
1646 }
1647 $this->tocCSSClass = $tocCSSClass;
1648 $this->tocAddReferences = $addReferences;
1649
1650 $this->opf->addItemRef("ref_" . Reference::TABLE_OF_CONTENTS, FALSE);
1651 $this->opf->addReference(Reference::TABLE_OF_CONTENTS, $title, $this->tocFileName);
1652
1653 if ($addToIndex) {
1654 $navPoint = new NavPoint($this->decodeHtmlEntities($title), $this->tocFileName, "ref_" . Reference::TABLE_OF_CONTENTS);
1655 $this->ncx->addNavPoint($navPoint);
1656 } else {
1657 $this->ncx->referencesList[Reference::TABLE_OF_CONTENTS] = $this->tocFileName;
1658 $this->ncx->referencesName[Reference::TABLE_OF_CONTENTS] = $title;
1659 }
1660 }
1661
1662 private function finalizeTOC() {
1663 if (!$this->buildTOC) {
1664 return FALSE;
1665 }
1666
1667 if (empty($this->tocTitle)) {
1668 $this->tocTitle = "Table of Contents";
1669 }
1670
1671 $tocData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1672
1673 if ($this->isEPubVersion2()) {
1674 $tocData .= "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n"
1675 . " \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
1676 . "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1677 . "<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
1678 } else {
1679 $tocData .= "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n"
1680 . "<head>\n<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n";
1681 }
1682
1683 if (!empty($this->tocCssFileName)) {
1684 $tocData .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"" . $this->tocCssFileName . "\" />\n";
1685 }
1686
1687 $tocData .= "<title>" . $this->tocTitle . "</title>\n"
1688 . "</head>\n"
1689 . "<body>\n"
1690 . "<h3>" . $this->tocTitle . "</h3>\n<div";
1691
1692 if (!empty($this->tocCSSClass)) {
1693 $tocData .= " class=\"" . $this->tocCSSClass . "\"";
1694 }
1695 $tocData .= ">\n";
1696
1697 while (list($item, $descriptive) = each($this->referencesOrder)) {
1698 if ($item === "text") {
1699 while (list($chapterName, $navPoint) = each($this->ncx->chapterList)) {
1700 $fileName = $navPoint->getContentSrc();
1701 $level = $navPoint->getLevel() -2;
1702 $tocData .= "\t<p>" . str_repeat(" &#160; &#160; &#160;", $level) . "<a href=\"" . $fileName . "\">" . $chapterName . "</a></p>\n";
1703 }
1704 } else if ($this->tocAddReferences === TRUE) {
1705 if (array_key_exists($item, $this->ncx->referencesList)) {
1706 $tocData .= "\t<p><a href=\"" . $this->ncx->referencesList[$item] . "\">" . $descriptive . "</a></p>\n";
1707 } else if ($item === "toc") {
1708 $tocData .= "\t<p><a href=\"TOC.xhtml\">" . $this->tocTitle . "</a></p>\n";
1709 } else if ($item === "cover" && $this->isCoverImageSet) {
1710 $tocData .= "\t<p><a href=\"CoverPage.xhtml\">" . $descriptive . "</a></p>\n";
1711 }
1712 }
1713 }
1714 $tocData .= "</div>\n</body>\n</html>\n";
1715
1716 $this->addReferencePage($this->tocTitle, $this->tocFileName, $tocData, Reference::TABLE_OF_CONTENTS);
1717
1718 }
1719
1720 /**
1721 * @return bool
1722 */
1723 function isEPubVersion2() {
1724 return $this->bookVersion === EPub::BOOK_VERSION_EPUB2;
1725 }
1726
1727 /**
1728 * @param string $cssFileName
1729 * @param string $title
1730 * @return string
1731 */
1732 function buildEPub3TOC($cssFileName = NULL, $title = "Table of Contents") {
1733 $this->ncx->referencesOrder = $this->referencesOrder;
1734 $this->ncx->setDocTitle($this->decodeHtmlEntities($this->title));
1735 return $this->ncx->finalizeEPub3($title, $cssFileName);
1736 }
1737
1738 /**
1739 * @param string $fileName
1740 * @param string $tocData
1741 * @return bool
1742 */
1743 function addEPub3TOC($fileName, $tocData) {
1744 if ($this->isEPubVersion2() || $this->isFinalized || array_key_exists($fileName, $this->fileList)) {
1745 return FALSE;
1746 }
1747 $fileName = Zip::getRelativePath($fileName);
1748 $fileName = preg_replace('#^[/\.]+#i', "", $fileName);
1749
1750 $this->zip->addFile($tocData, $this->bookRoot.$fileName);
1751
1752 $this->fileList[$fileName] = $fileName;
1753 $this->opf->addItem("toc", $fileName, "application/xhtml+xml", "nav");
1754 return TRUE;
1755 }
1756
1757 /**
1758 * Check for mandatory parameters and finalize the e-book.
1759 * Once finalized, the book is locked for further additions.
1760 *
1761 * @return bool $success
1762 */
1763 function finalize() {
1764 if ($this->isFinalized || $this->chapterCount == 0 || empty($this->title) || empty($this->language)) {
1765 return FALSE;
1766 }
1767
1768 if (empty($this->identifier) || empty($this->identifierType)) {
1769 $this->setIdentifier($this->createUUID(4), EPub::IDENTIFIER_UUID);
1770 }
1771
1772 if ($this->date == 0) {
1773 $this->date = time();
1774 }
1775
1776 if (empty($this->sourceURL)) {
1777 $this->sourceURL = $this->getCurrentPageURL();
1778 }
1779
1780 if (empty($this->publisherURL)) {
1781 $this->sourceURL = $this->getCurrentServerURL();
1782 }
1783
1784 // Generate OPF data:
1785 $this->opf->setIdent("BookId");
1786 $this->opf->initialize($this->title, $this->language, $this->identifier, $this->identifierType);
1787
1788 $DCdate = new DublinCore(DublinCore::DATE, gmdate($this->dateformat, $this->date));
1789 $DCdate->addOpfAttr("event", "publication");
1790 $this->opf->metadata->addDublinCore($DCdate);
1791
1792 if (!empty($this->description)) {
1793 $this->opf->addDCMeta(DublinCore::DESCRIPTION, $this->decodeHtmlEntities($this->description));
1794 }
1795
1796 if (!empty($this->publisherName)) {
1797 $this->opf->addDCMeta(DublinCore::PUBLISHER, $this->decodeHtmlEntities($this->publisherName));
1798 }
1799
1800 if (!empty($this->publisherURL)) {
1801 $this->opf->addDCMeta(DublinCore::RELATION, $this->decodeHtmlEntities($this->publisherURL));
1802 }
1803
1804 if (!empty($this->author)) {
1805 $author = $this->decodeHtmlEntities($this->author);
1806 $this->opf->addCreator($author, $this->decodeHtmlEntities($this->authorSortKey), MarcCode::AUTHOR);
1807 $this->ncx->setDocAuthor($author);
1808 }
1809
1810 if (!empty($this->rights)) {
1811 $this->opf->addDCMeta(DublinCore::RIGHTS, $this->decodeHtmlEntities($this->rights));
1812 }
1813
1814 if (!empty($this->coverage)) {
1815 $this->opf->addDCMeta(DublinCore::COVERAGE, $this->decodeHtmlEntities($this->coverage));
1816 }
1817
1818 if (!empty($this->sourceURL)) {
1819 $this->opf->addDCMeta(DublinCore::SOURCE, $this->sourceURL);
1820 }
1821
1822 if (!empty($this->relation)) {
1823 $this->opf->addDCMeta(DublinCore::RELATION, $this->decodeHtmlEntities($this->relation));
1824 }
1825
1826 if ($this->isCoverImageSet) {
1827 $this->opf->addMeta("cover", "coverImage");
1828 }
1829
1830 if (!empty($this->generator)) {
1831 $gen = $this->decodeHtmlEntities($this->generator);
1832 $this->opf->addMeta("generator", $gen);
1833 $this->ncx->addMetaEntry("dtb:generator", $gen);
1834 }
1835
1836 if ($this->EPubMark) {
1837 $this->opf->addMeta("generator", "EPub (Version " . self::VERSION . ") by A. Grandt, http://www.phpclasses.org/package/6115");
1838 }
1839
1840 reset($this->ncx->chapterList);
1841 list($firstChapterName, $firstChapterNavPoint) = each($this->ncx->chapterList);
1842 $firstChapterFileName = $firstChapterNavPoint->getContentSrc();
1843 $this->opf->addReference(Reference::TEXT, $this->decodeHtmlEntities($firstChapterName), $firstChapterFileName);
1844
1845 $this->ncx->setUid($this->identifier);
1846
1847 $this->ncx->setDocTitle($this->decodeHtmlEntities($this->title));
1848
1849 $this->ncx->referencesOrder = $this->referencesOrder;
1850 if ($this->isReferencesAddedToToc) {
1851 $this->ncx->finalizeReferences();
1852 }
1853
1854 $this->finalizeTOC();
1855
1856 if (!$this->isEPubVersion2()) {
1857 $this->addEPub3TOC("epub3toc.xhtml", $this->buildEPub3TOC());
1858 }
1859
1860 $opfFinal = $this->fixEncoding($this->opf->finalize());
1861 $ncxFinal = $this->fixEncoding($this->ncx->finalize());
1862
1863 if (mb_detect_encoding($opfFinal, 'UTF-8', true) === "UTF-8") {
1864 $this->zip->addFile($opfFinal, $this->bookRoot."book.opf");
1865 } else {
1866 $this->zip->addFile(mb_convert_encoding($opfFinal, "UTF-8"), $this->bookRoot."book.opf");
1867 }
1868
1869 if (mb_detect_encoding($ncxFinal, 'UTF-8', true) === "UTF-8") {
1870 $this->zip->addFile($ncxFinal, $this->bookRoot."book.ncx");
1871 } else {
1872 $this->zip->addFile(mb_convert_encoding($ncxFinal, "UTF-8"), $this->bookRoot."book.ncx");
1873 }
1874
1875 $this->opf = NULL;
1876 $this->ncx = NULL;
1877
1878 $this->isFinalized = TRUE;
1879 return TRUE;
1880 }
1881
1882 /**
1883 * Ensure the encoded string is a valid UTF-8 string.
1884 *
1885 * 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.
1886 *
1887 * @link: http://snippetdb.com/php/convert-string-to-utf-8-for-mysql
1888 * @param string $in_str
1889 * @return string converted string.
1890 */
1891 function fixEncoding($in_str) {
1892 if (mb_detect_encoding($in_str) == "UTF-8" && mb_check_encoding($in_str,"UTF-8")) {
1893 return $in_str;
1894 } else {
1895 return utf8_encode($in_str);
1896 }
1897 }
1898
1899 /**
1900 * Return the finalized book.
1901 *
1902 * @return string with the book in binary form.
1903 */
1904 function getBook() {
1905 if (!$this->isFinalized) {
1906 $this->finalize();
1907 }
1908
1909 return $this->zip->getZipData();
1910 }
1911
1912 /**
1913 * Remove disallowed characters from string to get a nearly safe filename
1914 *
1915 * @param string $fileName
1916 * @return mixed|string
1917 */
1918 function sanitizeFileName($fileName) {
1919 $fileName1 = str_replace($this->forbiddenCharacters, '', $fileName);
1920 $fileName2 = preg_replace('/[\s-]+/', '-', $fileName1);
1921 return trim($fileName2, '.-_');
1922
1923 }
1924
1925 /**
1926 * Cleanup the filepath, and remove leading . and / characters.
1927 *
1928 * Sometimes, when a path is generated from multiple fragments,
1929 * you can get something like "../data/html/../images/image.jpeg"
1930 * ePub files don't work well with that, this will normalize that
1931 * example path to "data/images/image.jpeg"
1932 *
1933 * @param string $fileName
1934 * @return string normalized filename
1935 */
1936 function normalizeFileName($fileName) {
1937 return preg_replace('#^[/\.]+#i', "", Zip::getRelativePath($fileName));
1938 }
1939
1940 /**
1941 * Save the ePub file to local disk.
1942 *
1943 * @param string $fileName
1944 * @param string $baseDir If empty baseDir is absolute to server path, if omitted it's relative to script path
1945 * @return The sent file name if successfull, FALSE if it failed.
1946 */
1947 function saveBook($fileName, $baseDir = '.') {
1948
1949 // Make fileName safe
1950 $fileName = $this->sanitizeFileName($fileName);
1951
1952 // Finalize book, if it's not done already
1953 if (!$this->isFinalized) {
1954 $this->finalize();
1955 }
1956
1957 if (stripos(strrev($fileName), "bupe.") !== 0) {
1958 $fileName .= ".epub";
1959 }
1960
1961 // Try to open file access
1962 $fh = fopen($baseDir.'/'.$fileName, "w");
1963
1964 if ($fh) {
1965 fputs($fh, $this->getBook());
1966 fclose($fh);
1967
1968 // if file is written return TRUE
1969 return $fileName;
1970 }
1971
1972 // return FALSE by default
1973 return FALSE;
1974 }
1975
1976 /**
1977 * Return the finalized book size.
1978 *
1979 * @return string
1980 */
1981 function getBookSize() {
1982 if (!$this->isFinalized) {
1983 $this->finalize();
1984 }
1985
1986 return $this->zip->getArchiveSize();
1987 }
1988
1989 /**
1990 * Send the book as a zip download
1991 *
1992 * Sending will fail if the output buffer is in use. You can override this limit by
1993 * calling setIgnoreEmptyBuffer(TRUE), though the function will still fail if that
1994 * buffer is not empty.
1995 *
1996 * @param string $fileName The name of the book without the .epub at the end.
1997 * @return The sent file name if successfull, FALSE if it failed.
1998 */
1999 function sendBook($fileName) {
2000 if (!$this->isFinalized) {
2001 $this->finalize();
2002 }
2003
2004 if (stripos(strrev($fileName), "bupe.") !== 0) {
2005 $fileName .= ".epub";
2006 }
2007
2008 if (TRUE === $this->zip->sendZip($fileName, "application/epub+zip")) {
2009 return $fileName;
2010 }
2011 return FALSE;
2012 }
2013
2014 /**
2015 * Generates an UUID.
2016 *
2017 * Default version (4) will generate a random UUID, version 3 will URL based UUID.
2018 *
2019 * Added for convinience
2020 *
2021 * @param int $bookVersion UUID version to retrieve, See lib.uuid.manual.html for details.
2022 * @param string $url
2023 * @return string The formatted uuid
2024 */
2025 function createUUID($bookVersion = 4, $url = NULL) {
2026 include_once("lib.uuid.php");
2027 return UUID::mint($bookVersion, $url, UUID::nsURL);
2028 }
2029
2030 /**
2031 * Get the url of the current page.
2032 * Example use: Default Source URL
2033 *
2034 * $return string Page URL.
2035 */
2036 function getCurrentPageURL() {
2037 $pageURL = $this->getCurrentServerURL() . filter_input(INPUT_SERVER, "REQUEST_URI");
2038 return $pageURL;
2039 }
2040
2041 /**
2042 * Get the url of the server.
2043 * Example use: Default Publisher URL
2044 *
2045 * $return string Server URL.
2046 */
2047 function getCurrentServerURL() {
2048 $serverURL = 'http';
2049 $https = filter_input(INPUT_SERVER, "HTTPS");
2050 $port = filter_input(INPUT_SERVER, "SERVER_PORT");
2051
2052 if ($https === "on") {
2053 $serverURL .= "s";
2054 }
2055 $serverURL .= "://" . filter_input(INPUT_SERVER, "SERVER_NAME");
2056 if ($port != "80") {
2057 $serverURL .= ":" . $port;
2058 }
2059 return $serverURL . '/';
2060 }
2061
2062 /**
2063 * Try to determine the mimetype of the file path.
2064 *
2065 * @param string $source Path
2066 * @return string mimetype, or FALSE.
2067 */
2068 function getMime($source) {
2069 return $this->mimetypes[pathinfo($source, PATHINFO_EXTENSION)];
2070 }
2071
2072 /**
2073 * Get an image from a file or url, return it resized if the image exceeds the $maxImageWidth or $maxImageHeight directives.
2074 *
2075 * The return value is an array.
2076 * ['width'] is the width of the image.
2077 * ['height'] is the height of the image.
2078 * ['mime'] is the mime type of the image. Resized images are always in jpeg format.
2079 * ['image'] is the image data.
2080 * ['ext'] is the extension of the image file.
2081 *
2082 * @param string $source path or url to file.
2083 * $return array
2084 */
2085 function getImage($source) {
2086 $width = -1;
2087 $height = -1;
2088 $mime = "application/octet-stream";
2089 $type = FALSE;
2090 $ext = "";
2091
2092
2093 $image = $this->getFileContents($source);
2094
2095 if ($image !== FALSE && strlen($image) > 0) {
2096 $imageFile = imagecreatefromstring($image);
2097 if ($imageFile !== false) {
2098 $width = ImageSX($imageFile);
2099 $height = ImageSY($imageFile);
2100 }
2101 if ($this->isExifInstalled) {
2102 @$type = exif_imagetype($source);
2103 $mime = image_type_to_mime_type($type);
2104 }
2105 if ($mime === "application/octet-stream") {
2106 $mime = $this->image_file_type_from_binary($image);
2107 }
2108 if ($mime === "application/octet-stream") {
2109 $mime = $this->getMimeTypeFromUrl($source);
2110 }
2111 } else {
2112 return FALSE;
2113 }
2114
2115 if ($width <= 0 || $height <= 0) {
2116 return FALSE;
2117 }
2118
2119 $ratio = 1;
2120
2121 if ($this->isGdInstalled) {
2122 if ($width > $this->maxImageWidth) {
2123 $ratio = $this->maxImageWidth/$width;
2124 }
2125 if ($height*$ratio > $this->maxImageHeight) {
2126 $ratio = $this->maxImageHeight/$height;
2127 }
2128
2129 if ($ratio < 1 || empty($mime) || ($this->isGifImagesEnabled !== FALSE && $mime == "image/gif")) {
2130 $image_o = imagecreatefromstring($image);
2131 $image_p = imagecreatetruecolor($width*$ratio, $height*$ratio);
2132
2133 if ($mime == "image/png") {
2134 imagealphablending($image_p, false);
2135 imagesavealpha($image_p, true);
2136 imagealphablending($image_o, true);
2137
2138 imagecopyresampled($image_p, $image_o, 0, 0, 0, 0, ($width*$ratio), ($height*$ratio), $width, $height);
2139 ob_start();
2140 imagepng($image_p, NULL, 9);
2141 $image = ob_get_contents();
2142 ob_end_clean();
2143
2144 $ext = "png";
2145 } else {
2146 imagecopyresampled($image_p, $image_o, 0, 0, 0, 0, ($width*$ratio), ($height*$ratio), $width, $height);
2147 ob_start();
2148 imagejpeg($image_p, NULL, 80);
2149 $image = ob_get_contents();
2150 ob_end_clean();
2151
2152 $mime = "image/jpeg";
2153 $ext = "jpg";
2154 }
2155 imagedestroy($image_o);
2156 imagedestroy($image_p);
2157 }
2158 }
2159
2160 if ($ext === "") {
2161 static $mimeToExt = array (
2162 'image/jpeg' => 'jpg',
2163 'image/gif' => 'gif',
2164 'image/png' => 'png'
2165 );
2166
2167 if (isset($mimeToExt[$mime])) {
2168 $ext = $mimeToExt[$mime];
2169 }
2170 }
2171
2172 $rv = array();
2173 $rv['width'] = $width*$ratio;
2174 $rv['height'] = $height*$ratio;
2175 $rv['mime'] = $mime;
2176 $rv['image'] = $image;
2177 $rv['ext'] = $ext;
2178
2179 return $rv;
2180 }
2181
2182 /**
2183 * Get file contents, using curl if available, else file_get_contents
2184 *
2185 * @param string $source
2186 * @return bool
2187 */
2188 function getFileContents($source, $toTempFile = FALSE) {
2189 $isExternal = preg_match('#^(http|ftp)s?://#i', $source) == 1;
2190
2191 if ($isExternal && $this->isCurlInstalled) {
2192 $ch = curl_init();
2193 $outFile = NULL;
2194 $fp = NULL;
2195 $res = FALSE;
2196 $info = array('http_code' => 500);
2197
2198 curl_setopt($ch, CURLOPT_HEADER, 0);
2199 curl_setopt($ch, CURLOPT_URL, str_replace(" ","%20",$source));
2200 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
2201 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2202 curl_setopt($ch, CURLOPT_BUFFERSIZE, 4096);
2203
2204 if ($toTempFile) {
2205 $outFile = tempnam(sys_get_temp_dir(), "EPub_v" . EPub::VERSION . "_");
2206 $fp = fopen($outFile, "w+b");
2207 curl_setopt($ch, CURLOPT_FILE, $fp);
2208
2209 $res = curl_exec($ch);
2210 $info = curl_getinfo($ch);
2211
2212 curl_close($ch);
2213 fclose($fp);
2214 } else {
2215 $res = curl_exec($ch);
2216 $info = curl_getinfo($ch);
2217
2218 curl_close($ch);
2219 }
2220
2221 if ($info['http_code'] == 200 && $res != false) {
2222 if ($toTempFile) {
2223 return $outFile;
2224 }
2225 return $res;
2226 }
2227 return FALSE;
2228 }
2229
2230 if ($this->isFileGetContentsInstalled && (!$isExternal || $this->isFileGetContentsExtInstalled)) {
2231 @$data = file_get_contents($source);
2232 return $data;
2233 }
2234 return FALSE;
2235 }
2236
2237 /**
2238 * get mime type from image data
2239 *
2240 * By fireweasel found on http://stackoverflow.com/questions/2207095/get-image-mimetype-from-resource-in-php-gd
2241 * @staticvar array $type
2242 * @param object $binary
2243 * @return string
2244 */
2245 function image_file_type_from_binary($binary) {
2246 $hits = 0;
2247 if (!preg_match(
2248 '/\A(?:(\xff\xd8\xff)|(GIF8[79]a)|(\x89PNG\x0d\x0a)|(BM)|(\x49\x49(?:\x2a\x00|\x00\x4a))|(FORM.{4}ILBM))/',
2249 $binary, $hits)) {
2250 return 'application/octet-stream';
2251 }
2252 static $type = array (
2253 1 => 'image/jpeg',
2254 2 => 'image/gif',
2255 3 => 'image/png',
2256 4 => 'image/x-windows-bmp',
2257 5 => 'image/tiff',
2258 6 => 'image/x-ilbm',
2259 );
2260 return $type[count($hits) - 1];
2261 }
2262
2263 /**
2264 * @param string $source URL Source
2265 * @return string MimeType
2266 */
2267 function getMimeTypeFromUrl($source) {
2268 $ext = FALSE;
2269
2270 $srev = strrev($source);
2271 $pos = strpos($srev, "?");
2272 if ($pos !== FALSE) {
2273 $srev = substr($srev, $pos+1);
2274 }
2275
2276 $pos = strpos($srev, ".");
2277 if ($pos !== FALSE) {
2278 $ext = strtolower(strrev(substr($srev, 0, $pos)));
2279 }
2280
2281 if ($ext !== FALSE) {
2282 return $this->getMimeTypeFromExtension($ext);
2283 }
2284 return "application/octet-stream";
2285 }
2286
2287 /**
2288 * @param string $ext Extension
2289 * @return string MimeType
2290 */
2291 function getMimeTypeFromExtension($ext) {
2292 switch ($ext) {
2293 case "jpg":
2294 case "jpe":
2295 case "jpeg":
2296 return 'image/jpeg';
2297 case "gif":
2298 return 'image/gif';
2299 case "png":
2300 return 'image/png';
2301 case "bmp":
2302 return 'image/x-windows-bmp';
2303 case "tif":
2304 case "tiff":
2305 case "cpt":
2306 return 'image/tiff';
2307 case "lbm":
2308 case "ilbm":
2309 return 'image/x-ilbm';
2310 default:
2311 return "application/octet-stream";
2312 }
2313 }
2314
2315 /**
2316 * Encode html code to use html entities, safeguarding it from potential character encoding peoblems
2317 * This function is a bit different from the vanilla htmlentities function in that it does not encode html tags.
2318 *
2319 * The regexp is taken from the PHP Manual discussion, it was written by user "busbyjon".
2320 * http://www.php.net/manual/en/function.htmlentities.php#90111
2321 *
2322 * @param string $string string to encode.
2323 */
2324 public function encodeHtml($string) {
2325 $string = strtr($string, $this->html_encoding_characters);
2326
2327 //return preg_replace("/&amp;(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,5};)/", "&\\1", $string);
2328 //return preg_replace("/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,5};)/", "&amp;", $string);
2329 return $string;
2330 }
2331
2332 /**
2333 * Helper function to create a DOM fragment with given markup.
2334 *
2335 * @author Adam Schmalhofer
2336 *
2337 * @param DOMDocument $dom
2338 * @param string $markup
2339 * @return DOMNode fragment in a node.
2340 */
2341 protected function createDomFragment($dom, $markup) {
2342 $node = $dom->createDocumentFragment();
2343 $node->appendXML($markup);
2344 return $node;
2345 }
2346
2347 /**
2348 * Retrieve an array of file names currently added to the book.
2349 * $key is the filename used in the book
2350 * $value is the original filename, will be the same as $key for most entries
2351 *
2352 * @return array file list
2353 */
2354 function getFileList() {
2355 return $this->fileList;
2356 }
2357
2358 /**
2359 * @deprecated Use Zip::getRelativePath($relPath) instead.
2360 */
2361 function relPath($relPath) {
2362 die ("Function was deprecated, use Zip::getRelativePath(\$relPath); instead");
2363 }
2364
2365 /**
2366 * Set default chapter target size.
2367 * Default is 250000 bytes, and minimum is 10240 bytes.
2368 *
2369 * @param int $size segment size in bytes
2370 * @return void
2371 */
2372 function setSplitSize($size) {
2373 $this->splitDefaultSize = (int)$size;
2374 if ($size < 10240) {
2375 $this->splitDefaultSize = 10240; // Making the file smaller than 10k is not a good idea.
2376 }
2377 }
2378
2379 /**
2380 * Get the chapter target size.
2381 *
2382 * @return $size
2383 */
2384 function getSplitSize() {
2385 return $this->splitDefaultSize;
2386 }
2387
2388 /**
2389 * Remove all non essential html tags and entities.
2390 *
2391 * @global type $htmlEntities
2392 * @param string $string
2393 * @return string with the stripped entities.
2394 */
2395 function decodeHtmlEntities($string) {
2396 global $htmlEntities;
2397
2398 $string = preg_replace('~\s*<br\s*/*\s*>\s*~i', "\n", $string);
2399 $string = preg_replace('~\s*</(p|div)\s*>\s*~i', "\n\n", $string);
2400 $string = preg_replace('~<[^>]*>~', '', $string);
2401
2402 $string = strtr($string, $htmlEntities);
2403
2404 $string = str_replace('&', '&amp;', $string);
2405 $string = str_replace('&amp;amp;', '&amp;', $string);
2406 $string = preg_replace('~&amp;(#x*[a-fA-F0-9]+;)~', '&\1', $string);
2407 $string = str_replace('<', '&lt;', $string);
2408 $string = str_replace('>', '&gt;', $string);
2409
2410 return $string;
2411 }
2412
2413 /**
2414 * Simply remove all HTML tags, brute force and no finesse.
2415 *
2416 * @param string $string html
2417 * @return string
2418 */
2419 function html2text($string) {
2420 return preg_replace('~<[^>]*>~', '', $string);
2421 }
2422
2423 /**
2424 * @return string
2425 */
2426 function getLog() {
2427 return $this->log->getLog();
2428 }
2429}
diff --git a/inc/3rdparty/libraries/PHPePub/EPubChapterSplitter.php b/inc/3rdparty/libraries/PHPePub/EPubChapterSplitter.php
new file mode 100644
index 00000000..1d44f238
--- /dev/null
+++ b/inc/3rdparty/libraries/PHPePub/EPubChapterSplitter.php
@@ -0,0 +1,201 @@
1<?php
2/**
3 * Split an HTML file into smaller html files, retaining the formatting and structure for the individual parts.
4 * What this splitter does is using DOM to try and retain any formatting in the file, including rebuilding the DOM tree for subsequent parts.
5 * Split size is considered max target size. The actual size is the result of an even split across the resulting files.
6 *
7 * @author A. Grandt <php@grandt.com>
8 * @copyright 2009-2014 A. Grandt
9 * @license GNU LGPL 2.1
10 * @link http://www.phpclasses.org/package/6115
11 * @link https://github.com/Grandt/PHPePub
12 * @version 3.20
13 */
14class EPubChapterSplitter {
15 const VERSION = 3.20;
16
17 private $splitDefaultSize = 250000;
18 private $bookVersion = EPub::BOOK_VERSION_EPUB2;
19
20 /**
21 *
22 * Enter description here ...
23 *
24 * @param unknown_type $ident
25 */
26 function setVersion($bookVersion) {
27 $this->bookVersion = is_string($bookVersion) ? trim($bookVersion) : EPub::BOOK_VERSION_EPUB2;
28 }
29
30 /**
31 * Set default chapter target size.
32 * Default is 250000 bytes, and minimum is 10240 bytes.
33 *
34 * @param $size segment size in bytes
35 * @return void
36 */
37 function setSplitSize($size) {
38 $this->splitDefaultSize = (int)$size;
39 if ($size < 10240) {
40 $this->splitDefaultSize = 10240; // Making the file smaller than 10k is not a good idea.
41 }
42 }
43
44 /**
45 * Get the chapter target size.
46 *
47 * @return $size
48 */
49 function getSplitSize() {
50 return $this->splitDefaultSize;
51 }
52
53 /**
54 * Split $chapter into multiple parts.
55 *
56 * The search string can either be a regular string or a PHP PECL Regular Expression pattern as defined here: http://www.php.net/manual/en/pcre.pattern.php
57 * If the search string is a regular string, the matching will be for lines in the HTML starting with the string given
58 *
59 * @param String $chapter XHTML file
60 * @param Bool $splitOnSearchString Split on chapter boundaries, Splitting on search strings disables the split size check.
61 * @param String $searchString Chapter string to search for can be fixed text, or a regular expression pattern.
62 *
63 * @return array with 1 or more parts
64 */
65 function splitChapter($chapter, $splitOnSearchString = false, $searchString = '/^Chapter\\ /i') {
66 $chapterData = array();
67 $isSearchRegexp = $splitOnSearchString && (preg_match('#^(\D|\S|\W).+\1[imsxeADSUXJu]*$#m', $searchString) == 1);
68 if ($splitOnSearchString && !$isSearchRegexp) {
69 $searchString = '#^<.+?>' . preg_quote($searchString, '#') . "#";
70 }
71
72 if (!$splitOnSearchString && strlen($chapter) <= $this->splitDefaultSize) {
73 return array($chapter);
74 }
75
76 $xmlDoc = new DOMDocument();
77 @$xmlDoc->loadHTML($chapter);
78
79 $head = $xmlDoc->getElementsByTagName("head");
80 $body = $xmlDoc->getElementsByTagName("body");
81
82 $htmlPos = stripos($chapter, "<html");
83 $htmlEndPos = stripos($chapter, ">", $htmlPos);
84 $newXML = substr($chapter, 0, $htmlEndPos+1) . "\n</html>";
85 if (strpos(trim($newXML), "<?xml ") === FALSE) {
86 $newXML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" . $newXML;
87 }
88 $headerLength = strlen($newXML);
89
90 $files = array();
91 $chapterNames = array();
92 $domDepth = 0;
93 $domPath = array();
94 $domClonedPath = array();
95
96 $curFile = $xmlDoc->createDocumentFragment();
97 $files[] = $curFile;
98 $curParent = $curFile;
99 $curSize = 0;
100
101 $bodyLen = strlen($xmlDoc->saveXML($body->item(0)));
102 $headLen = strlen($xmlDoc->saveXML($head->item(0))) + $headerLength;
103
104 $partSize = $this->splitDefaultSize - $headLen;
105
106 if ($bodyLen > $partSize) {
107 $parts = ceil($bodyLen / $partSize);
108 $partSize = ($bodyLen / $parts) - $headLen;
109 }
110
111 $node = $body->item(0)->firstChild;
112
113 do {
114 $nodeData = $xmlDoc->saveXML($node);
115 $nodeLen = strlen($nodeData);
116
117 if ($nodeLen > $partSize && $node->hasChildNodes()) {
118 $domPath[] = $node;
119 $domClonedPath[] = $node->cloneNode(false);
120 $domDepth++;
121
122 $node = $node->firstChild;
123 }
124
125 $node2 = $node->nextSibling;
126
127 if ($node != null && $node->nodeName != "#text") {
128 $doSplit = false;
129 if ($splitOnSearchString) {
130 $doSplit = preg_match($searchString, $nodeData) == 1;
131 if ($doSplit) {
132 $chapterNames[] = trim($nodeData);
133 }
134 }
135
136 if ($curSize > 0 && ($doSplit || (!$splitOnSearchString && $curSize + $nodeLen > $partSize))) {
137 $curFile = $xmlDoc->createDocumentFragment();
138 $files[] = $curFile;
139 $curParent = $curFile;
140 if ($domDepth > 0) {
141 reset($domPath);
142 reset($domClonedPath);
143 $oneDomClonedPath = each($domClonedPath);
144 while ($oneDomClonedPath) {
145 list($k, $v) = $oneDomClonedPath;
146 $newParent = $v->cloneNode(false);
147 $curParent->appendChild($newParent);
148 $curParent = $newParent;
149 $oneDomClonedPath = each($domClonedPath);
150 }
151 }
152 $curSize = strlen($xmlDoc->saveXML($curFile));
153 }
154 $curParent->appendChild($node->cloneNode(true));
155 $curSize += $nodeLen;
156 }
157
158 $node = $node2;
159 while ($node == null && $domDepth > 0) {
160 $domDepth--;
161 $node = end($domPath)->nextSibling;
162 array_pop($domPath);
163 array_pop($domClonedPath);
164 $curParent = $curParent->parentNode;
165 }
166 } while ($node != null);
167
168 $curFile = null;
169 $curSize = 0;
170
171 $xml = new DOMDocument('1.0', $xmlDoc->xmlEncoding);
172 $xml->lookupPrefix("http://www.w3.org/1999/xhtml");
173 $xml->preserveWhiteSpace = false;
174 $xml->formatOutput = true;
175
176 for ($idx = 0; $idx < count($files); $idx++) {
177 $xml2Doc = new DOMDocument('1.0', $xmlDoc->xmlEncoding);
178 $xml2Doc->lookupPrefix("http://www.w3.org/1999/xhtml");
179 $xml2Doc->loadXML($newXML);
180 $html = $xml2Doc->getElementsByTagName("html")->item(0);
181 $html->appendChild($xml2Doc->importNode($head->item(0), true));
182 $body = $xml2Doc->createElement("body");
183 $html->appendChild($body);
184 $body->appendChild($xml2Doc->importNode($files[$idx], true));
185
186 // force pretty printing and correct formatting, should not be needed, but it is.
187 $xml->loadXML($xml2Doc->saveXML());
188
189 $doc = $xml->saveXML();
190
191 if ($this->bookVersion === EPub::BOOK_VERSION_EPUB3) {
192 $doc = preg_replace('#^\s*<!DOCTYPE\ .+?>\s*#im', '', $doc);
193 }
194
195 $chapterData[$splitOnSearchString ? $chapterNames[$idx] : $idx] = $doc;
196 }
197
198 return $chapterData;
199 }
200}
201?>
diff --git a/inc/3rdparty/libraries/PHPePub/Logger.php b/inc/3rdparty/libraries/PHPePub/Logger.php
new file mode 100644
index 00000000..314019cb
--- /dev/null
+++ b/inc/3rdparty/libraries/PHPePub/Logger.php
@@ -0,0 +1,92 @@
1<?php
2/**
3 * Simple log line aggregator.
4 *
5 * @author A. Grandt <php@grandt.com>
6 * @copyright 2012-2013 A. Grandt
7 * @license GNU LGPL, Attribution required for commercial implementations, requested for everything else.
8 * @version 1.00
9 */
10class Logger {
11 const VERSION = 1.00;
12
13 private $log = "";
14 private $tStart;
15 private $tLast;
16 private $name = NULL;
17 private $isLogging = FALSE;
18 private $isDebugging = FALSE;
19
20 /**
21 * Class constructor.
22 *
23 * @return void
24 */
25 function __construct($name = NULL, $isLogging = FALSE) {
26 if ($name === NULL) {
27 $this->name = "";
28 } else {
29 $this->name = $name . " : ";
30 }
31 $this->isLogging = $isLogging;
32 $this->start();
33 }
34
35 /**
36 * Class destructor
37 *
38 * @return void
39 * @TODO make sure elements in the destructor match the current class elements
40 */
41 function __destruct() {
42 unset($this->log);
43 }
44
45 function start() {
46 /* Prepare Logging. Just in case it's used. later */
47 if ($this->isLogging) {
48 $this->tStart = gettimeofday();
49 $this->tLast = $this->tStart;
50 $this->log = "<h1>Log: " . $this->name . "</h1>\n<pre>Started: " . gmdate("D, d M Y H:i:s T", $this->tStart['sec']) . "\n &#916; Start ; &#916; Last ;";
51 $this->logLine("Start");
52 }
53 }
54
55 function dumpInstalledModules() {
56 if ($this->isLogging) {
57 $isCurlInstalled = extension_loaded('curl') && function_exists('curl_version');
58 $isGdInstalled = extension_loaded('gd') && function_exists('gd_info');
59 $isExifInstalled = extension_loaded('exif') && function_exists('exif_imagetype');
60 $isFileGetContentsInstalled = function_exists('file_get_contents');
61 $isFileGetContentsExtInstalled = $isFileGetContentsInstalled && ini_get('allow_url_fopen');
62
63 $this->logLine("isCurlInstalled...............: " . ($isCurlInstalled ? "Yes" : "No"));
64 $this->logLine("isGdInstalled.................: " . ($isGdInstalled ? "Yes" : "No"));
65 $this->logLine("isExifInstalled...............: " . ($isExifInstalled ? "Yes" : "No"));
66 $this->logLine("isFileGetContentsInstalled....: " . ($isFileGetContentsInstalled ? "Yes" : "No"));
67 $this->logLine("isFileGetContentsExtInstalled.: " . ($isFileGetContentsExtInstalled ? "Yes" : "No"));
68 }
69 }
70
71 function logLine($line) {
72 if ($this->isLogging) {
73 $tTemp = gettimeofday();
74 $tS = $this->tStart['sec'] + (((int)($this->tStart['usec']/100))/10000);
75 $tL = $this->tLast['sec'] + (((int)($this->tLast['usec']/100))/10000);
76 $tT = $tTemp['sec'] + (((int)($tTemp['usec']/100))/10000);
77
78 $logline = sprintf("\n+%08.04f; +%08.04f; ", ($tT-$tS), ($tT-$tL)) . $this->name . $line;
79 $this->log .= $logline;
80 $this->tLast = $tTemp;
81
82 if ($this->isDebugging) {
83 echo "<pre>" . $logline . "\n</pre>\n";
84 }
85 }
86 }
87
88 function getLog() {
89 return $this->log;
90 }
91}
92?> \ No newline at end of file
diff --git a/inc/3rdparty/libraries/PHPePub/Zip.php b/inc/3rdparty/libraries/PHPePub/Zip.php
new file mode 100644
index 00000000..01e03566
--- /dev/null
+++ b/inc/3rdparty/libraries/PHPePub/Zip.php
@@ -0,0 +1,818 @@
1<?php
2/**
3 * Class to create and manage a Zip file.
4 *
5 * Initially inspired by CreateZipFile by Rochak Chauhan www.rochakchauhan.com (http://www.phpclasses.org/browse/package/2322.html)
6 * and
7 * http://www.pkware.com/documents/casestudies/APPNOTE.TXT Zip file specification.
8 *
9 * License: GNU LGPL, Attribution required for commercial implementations, requested for everything else.
10 *
11 * @author A. Grandt <php@grandt.com>
12 * @copyright 2009-2014 A. Grandt
13 * @license GNU LGPL 2.1
14 * @link http://www.phpclasses.org/package/6110
15 * @link https://github.com/Grandt/PHPZip
16 * @version 1.60
17 */
18class Zip {
19 const VERSION = 1.60;
20
21 const ZIP_LOCAL_FILE_HEADER = "\x50\x4b\x03\x04"; // Local file header signature
22 const ZIP_CENTRAL_FILE_HEADER = "\x50\x4b\x01\x02"; // Central file header signature
23 const ZIP_END_OF_CENTRAL_DIRECTORY = "\x50\x4b\x05\x06\x00\x00\x00\x00"; //end of Central directory record
24
25 const EXT_FILE_ATTR_DIR = 010173200020; // Permission 755 drwxr-xr-x = (((S_IFDIR | 0755) << 16) | S_DOS_D);
26 const EXT_FILE_ATTR_FILE = 020151000040; // Permission 644 -rw-r--r-- = (((S_IFREG | 0644) << 16) | S_DOS_A);
27
28 const ATTR_VERSION_TO_EXTRACT = "\x14\x00"; // Version needed to extract
29 const ATTR_MADE_BY_VERSION = "\x1E\x03"; // Made By Version
30
31 // Unix file types
32 const S_IFIFO = 0010000; // named pipe (fifo)
33 const S_IFCHR = 0020000; // character special
34 const S_IFDIR = 0040000; // directory
35 const S_IFBLK = 0060000; // block special
36 const S_IFREG = 0100000; // regular
37 const S_IFLNK = 0120000; // symbolic link
38 const S_IFSOCK = 0140000; // socket
39
40 // setuid/setgid/sticky bits, the same as for chmod:
41
42 const S_ISUID = 0004000; // set user id on execution
43 const S_ISGID = 0002000; // set group id on execution
44 const S_ISTXT = 0001000; // sticky bit
45
46 // And of course, the other 12 bits are for the permissions, the same as for chmod:
47 // When addding these up, you can also just write the permissions as a simgle octal number
48 // ie. 0755. The leading 0 specifies octal notation.
49 const S_IRWXU = 0000700; // RWX mask for owner
50 const S_IRUSR = 0000400; // R for owner
51 const S_IWUSR = 0000200; // W for owner
52 const S_IXUSR = 0000100; // X for owner
53 const S_IRWXG = 0000070; // RWX mask for group
54 const S_IRGRP = 0000040; // R for group
55 const S_IWGRP = 0000020; // W for group
56 const S_IXGRP = 0000010; // X for group
57 const S_IRWXO = 0000007; // RWX mask for other
58 const S_IROTH = 0000004; // R for other
59 const S_IWOTH = 0000002; // W for other
60 const S_IXOTH = 0000001; // X for other
61 const S_ISVTX = 0001000; // save swapped text even after use
62
63 // Filetype, sticky and permissions are added up, and shifted 16 bits left BEFORE adding the DOS flags.
64
65 // DOS file type flags, we really only use the S_DOS_D flag.
66
67 const S_DOS_A = 0000040; // DOS flag for Archive
68 const S_DOS_D = 0000020; // DOS flag for Directory
69 const S_DOS_V = 0000010; // DOS flag for Volume
70 const S_DOS_S = 0000004; // DOS flag for System
71 const S_DOS_H = 0000002; // DOS flag for Hidden
72 const S_DOS_R = 0000001; // DOS flag for Read Only
73
74 private $zipMemoryThreshold = 1048576; // Autocreate tempfile if the zip data exceeds 1048576 bytes (1 MB)
75
76 private $zipData = NULL;
77 private $zipFile = NULL;
78 private $zipComment = NULL;
79 private $cdRec = array(); // central directory
80 private $offset = 0;
81 private $isFinalized = FALSE;
82 private $addExtraField = TRUE;
83
84 private $streamChunkSize = 65536;
85 private $streamFilePath = NULL;
86 private $streamTimestamp = NULL;
87 private $streamFileComment = NULL;
88 private $streamFile = NULL;
89 private $streamData = NULL;
90 private $streamFileLength = 0;
91 private $streamExtFileAttr = null;
92
93 /**
94 * Constructor.
95 *
96 * @param boolean $useZipFile Write temp zip data to tempFile? Default FALSE
97 */
98 function __construct($useZipFile = FALSE) {
99 if ($useZipFile) {
100 $this->zipFile = tmpfile();
101 } else {
102 $this->zipData = "";
103 }
104 }
105
106 function __destruct() {
107 if (is_resource($this->zipFile)) {
108 fclose($this->zipFile);
109 }
110 $this->zipData = NULL;
111 }
112
113 /**
114 * Extra fields on the Zip directory records are Unix time codes needed for compatibility on the default Mac zip archive tool.
115 * These are enabled as default, as they do no harm elsewhere and only add 26 bytes per file added.
116 *
117 * @param bool $setExtraField TRUE (default) will enable adding of extra fields, anything else will disable it.
118 */
119 function setExtraField($setExtraField = TRUE) {
120 $this->addExtraField = ($setExtraField === TRUE);
121 }
122
123 /**
124 * Set Zip archive comment.
125 *
126 * @param string $newComment New comment. NULL to clear.
127 * @return bool $success
128 */
129 public function setComment($newComment = NULL) {
130 if ($this->isFinalized) {
131 return FALSE;
132 }
133 $this->zipComment = $newComment;
134
135 return TRUE;
136 }
137
138 /**
139 * Set zip file to write zip data to.
140 * This will cause all present and future data written to this class to be written to this file.
141 * This can be used at any time, even after the Zip Archive have been finalized. Any previous file will be closed.
142 * Warning: If the given file already exists, it will be overwritten.
143 *
144 * @param string $fileName
145 * @return bool $success
146 */
147 public function setZipFile($fileName) {
148 if (is_file($fileName)) {
149 unlink($fileName);
150 }
151 $fd=fopen($fileName, "x+b");
152 if (is_resource($this->zipFile)) {
153 rewind($this->zipFile);
154 while (!feof($this->zipFile)) {
155 fwrite($fd, fread($this->zipFile, $this->streamChunkSize));
156 }
157
158 fclose($this->zipFile);
159 } else {
160 fwrite($fd, $this->zipData);
161 $this->zipData = NULL;
162 }
163 $this->zipFile = $fd;
164
165 return TRUE;
166 }
167
168 /**
169 * Add an empty directory entry to the zip archive.
170 * Basically this is only used if an empty directory is added.
171 *
172 * @param string $directoryPath Directory Path and name to be added to the archive.
173 * @param int $timestamp (Optional) Timestamp for the added directory, if omitted or set to 0, the current time will be used.
174 * @param string $fileComment (Optional) Comment to be added to the archive for this directory. To use fileComment, timestamp must be given.
175 * @param int $extFileAttr (Optional) The external file reference, use generateExtAttr to generate this.
176 * @return bool $success
177 */
178 public function addDirectory($directoryPath, $timestamp = 0, $fileComment = NULL, $extFileAttr = self::EXT_FILE_ATTR_DIR) {
179 if ($this->isFinalized) {
180 return FALSE;
181 }
182 $directoryPath = str_replace("\\", "/", $directoryPath);
183 $directoryPath = rtrim($directoryPath, "/");
184
185 if (strlen($directoryPath) > 0) {
186 $this->buildZipEntry($directoryPath.'/', $fileComment, "\x00\x00", "\x00\x00", $timestamp, "\x00\x00\x00\x00", 0, 0, $extFileAttr);
187 return TRUE;
188 }
189 return FALSE;
190 }
191
192 /**
193 * Add a file to the archive at the specified location and file name.
194 *
195 * @param string $data File data.
196 * @param string $filePath Filepath and name to be used in the archive.
197 * @param int $timestamp (Optional) Timestamp for the added file, if omitted or set to 0, the current time will be used.
198 * @param string $fileComment (Optional) Comment to be added to the archive for this file. To use fileComment, timestamp must be given.
199 * @param bool $compress (Optional) Compress file, if set to FALSE the file will only be stored. Default TRUE.
200 * @param int $extFileAttr (Optional) The external file reference, use generateExtAttr to generate this.
201 * @return bool $success
202 */
203 public function addFile($data, $filePath, $timestamp = 0, $fileComment = NULL, $compress = TRUE, $extFileAttr = self::EXT_FILE_ATTR_FILE) {
204 if ($this->isFinalized) {
205 return FALSE;
206 }
207
208 if (is_resource($data) && get_resource_type($data) == "stream") {
209 $this->addLargeFile($data, $filePath, $timestamp, $fileComment, $extFileAttr);
210 return FALSE;
211 }
212
213 $gzData = "";
214 $gzType = "\x08\x00"; // Compression type 8 = deflate
215 $gpFlags = "\x00\x00"; // General Purpose bit flags for compression type 8 it is: 0=Normal, 1=Maximum, 2=Fast, 3=super fast compression.
216 $dataLength = strlen($data);
217 $fileCRC32 = pack("V", crc32($data));
218
219 if ($compress) {
220 $gzTmp = gzcompress($data);
221 $gzData = substr(substr($gzTmp, 0, strlen($gzTmp) - 4), 2); // gzcompress adds a 2 byte header and 4 byte CRC we can't use.
222 // The 2 byte header does contain useful data, though in this case the 2 parameters we'd be interrested in will always be 8 for compression type, and 2 for General purpose flag.
223 $gzLength = strlen($gzData);
224 } else {
225 $gzLength = $dataLength;
226 }
227
228 if ($gzLength >= $dataLength) {
229 $gzLength = $dataLength;
230 $gzData = $data;
231 $gzType = "\x00\x00"; // Compression type 0 = stored
232 $gpFlags = "\x00\x00"; // Compression type 0 = stored
233 }
234
235 if (!is_resource($this->zipFile) && ($this->offset + $gzLength) > $this->zipMemoryThreshold) {
236 $this->zipflush();
237 }
238
239 $this->buildZipEntry($filePath, $fileComment, $gpFlags, $gzType, $timestamp, $fileCRC32, $gzLength, $dataLength, $extFileAttr);
240
241 $this->zipwrite($gzData);
242
243 return TRUE;
244 }
245
246 /**
247 * Add the content to a directory.
248 *
249 * @author Adam Schmalhofer <Adam.Schmalhofer@gmx.de>
250 * @author A. Grandt
251 *
252 * @param string $realPath Path on the file system.
253 * @param string $zipPath Filepath and name to be used in the archive.
254 * @param bool $recursive Add content recursively, default is TRUE.
255 * @param bool $followSymlinks Follow and add symbolic links, if they are accessible, default is TRUE.
256 * @param array &$addedFiles Reference to the added files, this is used to prevent duplicates, efault is an empty array.
257 * If you start the function by parsing an array, the array will be populated with the realPath
258 * and zipPath kay/value pairs added to the archive by the function.
259 * @param bool $overrideFilePermissions Force the use of the file/dir permissions set in the $extDirAttr
260 * and $extFileAttr parameters.
261 * @param int $extDirAttr Permissions for directories.
262 * @param int $extFileAttr Permissions for files.
263 */
264 public function addDirectoryContent($realPath, $zipPath, $recursive = TRUE, $followSymlinks = TRUE, &$addedFiles = array(),
265 $overrideFilePermissions = FALSE, $extDirAttr = self::EXT_FILE_ATTR_DIR, $extFileAttr = self::EXT_FILE_ATTR_FILE) {
266 if (file_exists($realPath) && !isset($addedFiles[realpath($realPath)])) {
267 if (is_dir($realPath)) {
268 if ($overrideFilePermissions) {
269 $this->addDirectory($zipPath, 0, null, $extDirAttr);
270 } else {
271 $this->addDirectory($zipPath, 0, null, self::getFileExtAttr($realPath));
272 }
273 }
274
275 $addedFiles[realpath($realPath)] = $zipPath;
276
277 $iter = new DirectoryIterator($realPath);
278 foreach ($iter as $file) {
279 if ($file->isDot()) {
280 continue;
281 }
282 $newRealPath = $file->getPathname();
283 $newZipPath = self::pathJoin($zipPath, $file->getFilename());
284
285 if (file_exists($newRealPath) && ($followSymlinks === TRUE || !is_link($newRealPath))) {
286 if ($file->isFile()) {
287 $addedFiles[realpath($newRealPath)] = $newZipPath;
288 if ($overrideFilePermissions) {
289 $this->addLargeFile($newRealPath, $newZipPath, 0, null, $extFileAttr);
290 } else {
291 $this->addLargeFile($newRealPath, $newZipPath, 0, null, self::getFileExtAttr($newRealPath));
292 }
293 } else if ($recursive === TRUE) {
294 $this->addDirectoryContent($newRealPath, $newZipPath, $recursive, $followSymlinks, $addedFiles, $overrideFilePermissions, $extDirAttr, $extFileAttr);
295 } else {
296 if ($overrideFilePermissions) {
297 $this->addDirectory($zipPath, 0, null, $extDirAttr);
298 } else {
299 $this->addDirectory($zipPath, 0, null, self::getFileExtAttr($newRealPath));
300 }
301 }
302 }
303 }
304 }
305 }
306
307 /**
308 * Add a file to the archive at the specified location and file name.
309 *
310 * @param string $dataFile File name/path.
311 * @param string $filePath Filepath and name to be used in the archive.
312 * @param int $timestamp (Optional) Timestamp for the added file, if omitted or set to 0, the current time will be used.
313 * @param string $fileComment (Optional) Comment to be added to the archive for this file. To use fileComment, timestamp must be given.
314 * @param int $extFileAttr (Optional) The external file reference, use generateExtAttr to generate this.
315 * @return bool $success
316 */
317 public function addLargeFile($dataFile, $filePath, $timestamp = 0, $fileComment = NULL, $extFileAttr = self::EXT_FILE_ATTR_FILE) {
318 if ($this->isFinalized) {
319 return FALSE;
320 }
321
322 if (is_string($dataFile) && is_file($dataFile)) {
323 $this->processFile($dataFile, $filePath, $timestamp, $fileComment, $extFileAttr);
324 } else if (is_resource($dataFile) && get_resource_type($dataFile) == "stream") {
325 $fh = $dataFile;
326 $this->openStream($filePath, $timestamp, $fileComment, $extFileAttr);
327
328 while (!feof($fh)) {
329 $this->addStreamData(fread($fh, $this->streamChunkSize));
330 }
331 $this->closeStream($this->addExtraField);
332 }
333 return TRUE;
334 }
335
336 /**
337 * Create a stream to be used for large entries.
338 *
339 * @param string $filePath Filepath and name to be used in the archive.
340 * @param int $timestamp (Optional) Timestamp for the added file, if omitted or set to 0, the current time will be used.
341 * @param string $fileComment (Optional) Comment to be added to the archive for this file. To use fileComment, timestamp must be given.
342 * @param int $extFileAttr (Optional) The external file reference, use generateExtAttr to generate this.
343 * @return bool $success
344 */
345 public function openStream($filePath, $timestamp = 0, $fileComment = null, $extFileAttr = self::EXT_FILE_ATTR_FILE) {
346 if (!function_exists('sys_get_temp_dir')) {
347 die ("ERROR: Zip " . self::VERSION . " requires PHP version 5.2.1 or above if large files are used.");
348 }
349
350 if ($this->isFinalized) {
351 return FALSE;
352 }
353
354 $this->zipflush();
355
356 if (strlen($this->streamFilePath) > 0) {
357 $this->closeStream();
358 }
359
360 $this->streamFile = tempnam(sys_get_temp_dir(), 'Zip');
361 $this->streamData = fopen($this->streamFile, "wb");
362 $this->streamFilePath = $filePath;
363 $this->streamTimestamp = $timestamp;
364 $this->streamFileComment = $fileComment;
365 $this->streamFileLength = 0;
366 $this->streamExtFileAttr = $extFileAttr;
367
368 return TRUE;
369 }
370
371 /**
372 * Add data to the open stream.
373 *
374 * @param string $data
375 * @return mixed length in bytes added or FALSE if the archive is finalized or there are no open stream.
376 */
377 public function addStreamData($data) {
378 if ($this->isFinalized || strlen($this->streamFilePath) == 0) {
379 return FALSE;
380 }
381
382 $length = fwrite($this->streamData, $data, strlen($data));
383 if ($length != strlen($data)) {
384 die ("<p>Length mismatch</p>\n");
385 }
386 $this->streamFileLength += $length;
387
388 return $length;
389 }
390
391 /**
392 * Close the current stream.
393 *
394 * @return bool $success
395 */
396 public function closeStream() {
397 if ($this->isFinalized || strlen($this->streamFilePath) == 0) {
398 return FALSE;
399 }
400
401 fflush($this->streamData);
402 fclose($this->streamData);
403
404 $this->processFile($this->streamFile, $this->streamFilePath, $this->streamTimestamp, $this->streamFileComment, $this->streamExtFileAttr);
405
406 $this->streamData = null;
407 $this->streamFilePath = null;
408 $this->streamTimestamp = null;
409 $this->streamFileComment = null;
410 $this->streamFileLength = 0;
411 $this->streamExtFileAttr = null;
412
413 // Windows is a little slow at times, so a millisecond later, we can unlink this.
414 unlink($this->streamFile);
415
416 $this->streamFile = null;
417
418 return TRUE;
419 }
420
421 private function processFile($dataFile, $filePath, $timestamp = 0, $fileComment = null, $extFileAttr = self::EXT_FILE_ATTR_FILE) {
422 if ($this->isFinalized) {
423 return FALSE;
424 }
425
426 $tempzip = tempnam(sys_get_temp_dir(), 'ZipStream');
427
428 $zip = new ZipArchive;
429 if ($zip->open($tempzip) === TRUE) {
430 $zip->addFile($dataFile, 'file');
431 $zip->close();
432 }
433
434 $file_handle = fopen($tempzip, "rb");
435 $stats = fstat($file_handle);
436 $eof = $stats['size']-72;
437
438 fseek($file_handle, 6);
439
440 $gpFlags = fread($file_handle, 2);
441 $gzType = fread($file_handle, 2);
442 fread($file_handle, 4);
443 $fileCRC32 = fread($file_handle, 4);
444 $v = unpack("Vval", fread($file_handle, 4));
445 $gzLength = $v['val'];
446 $v = unpack("Vval", fread($file_handle, 4));
447 $dataLength = $v['val'];
448
449 $this->buildZipEntry($filePath, $fileComment, $gpFlags, $gzType, $timestamp, $fileCRC32, $gzLength, $dataLength, $extFileAttr);
450
451 fseek($file_handle, 34);
452 $pos = 34;
453
454 while (!feof($file_handle) && $pos < $eof) {
455 $datalen = $this->streamChunkSize;
456 if ($pos + $this->streamChunkSize > $eof) {
457 $datalen = $eof-$pos;
458 }
459 $data = fread($file_handle, $datalen);
460 $pos += $datalen;
461
462 $this->zipwrite($data);
463 }
464
465 fclose($file_handle);
466
467 unlink($tempzip);
468 }
469
470 /**
471 * Close the archive.
472 * A closed archive can no longer have new files added to it.
473 *
474 * @return bool $success
475 */
476 public function finalize() {
477 if (!$this->isFinalized) {
478 if (strlen($this->streamFilePath) > 0) {
479 $this->closeStream();
480 }
481 $cd = implode("", $this->cdRec);
482
483 $cdRecSize = pack("v", sizeof($this->cdRec));
484 $cdRec = $cd . self::ZIP_END_OF_CENTRAL_DIRECTORY
485 . $cdRecSize . $cdRecSize
486 . pack("VV", strlen($cd), $this->offset);
487 if (!empty($this->zipComment)) {
488 $cdRec .= pack("v", strlen($this->zipComment)) . $this->zipComment;
489 } else {
490 $cdRec .= "\x00\x00";
491 }
492
493 $this->zipwrite($cdRec);
494
495 $this->isFinalized = TRUE;
496 $this->cdRec = NULL;
497
498 return TRUE;
499 }
500 return FALSE;
501 }
502
503 /**
504 * Get the handle ressource for the archive zip file.
505 * If the zip haven't been finalized yet, this will cause it to become finalized
506 *
507 * @return zip file handle
508 */
509 public function getZipFile() {
510 if (!$this->isFinalized) {
511 $this->finalize();
512 }
513
514 $this->zipflush();
515
516 rewind($this->zipFile);
517
518 return $this->zipFile;
519 }
520
521 /**
522 * Get the zip file contents
523 * If the zip haven't been finalized yet, this will cause it to become finalized
524 *
525 * @return zip data
526 */
527 public function getZipData() {
528 if (!$this->isFinalized) {
529 $this->finalize();
530 }
531 if (!is_resource($this->zipFile)) {
532 return $this->zipData;
533 } else {
534 rewind($this->zipFile);
535 $filestat = fstat($this->zipFile);
536 return fread($this->zipFile, $filestat['size']);
537 }
538 }
539
540 /**
541 * Send the archive as a zip download
542 *
543 * @param String $fileName The name of the Zip archive, in ISO-8859-1 (or ASCII) encoding, ie. "archive.zip". Optional, defaults to NULL, which means that no ISO-8859-1 encoded file name will be specified.
544 * @param String $contentType Content mime type. Optional, defaults to "application/zip".
545 * @param String $utf8FileName The name of the Zip archive, in UTF-8 encoding. Optional, defaults to NULL, which means that no UTF-8 encoded file name will be specified.
546 * @param bool $inline Use Content-Disposition with "inline" instead of "attached". Optional, defaults to FALSE.
547 * @return bool $success
548 */
549 function sendZip($fileName = null, $contentType = "application/zip", $utf8FileName = null, $inline = false) {
550 if (!$this->isFinalized) {
551 $this->finalize();
552 }
553
554 $headerFile = null;
555 $headerLine = null;
556 if (!headers_sent($headerFile, $headerLine) or die("<p><strong>Error:</strong> Unable to send file $fileName. HTML Headers have already been sent from <strong>$headerFile</strong> in line <strong>$headerLine</strong></p>")) {
557 if ((ob_get_contents() === FALSE || ob_get_contents() == '') or die("\n<p><strong>Error:</strong> Unable to send file <strong>$fileName</strong>. Output buffer contains the following text (typically warnings or errors):<br>" . htmlentities(ob_get_contents()) . "</p>")) {
558 if (ini_get('zlib.output_compression')) {
559 ini_set('zlib.output_compression', 'Off');
560 }
561
562 header("Pragma: public");
563 header("Last-Modified: " . gmdate("D, d M Y H:i:s T"));
564 header("Expires: 0");
565 header("Accept-Ranges: bytes");
566 header("Connection: close");
567 header("Content-Type: " . $contentType);
568 $cd = "Content-Disposition: ";
569 if ($inline) {
570 $cd .= "inline";
571 } else{
572 $cd .= "attached";
573 }
574 if ($fileName) {
575 $cd .= '; filename="' . $fileName . '"';
576 }
577 if ($utf8FileName) {
578 $cd .= "; filename*=UTF-8''" . rawurlencode($utf8FileName);
579 }
580 header($cd);
581 header("Content-Length: ". $this->getArchiveSize());
582
583 if (!is_resource($this->zipFile)) {
584 echo $this->zipData;
585 } else {
586 rewind($this->zipFile);
587
588 while (!feof($this->zipFile)) {
589 echo fread($this->zipFile, $this->streamChunkSize);
590 }
591 }
592 }
593 return TRUE;
594 }
595 return FALSE;
596 }
597
598 /**
599 * Return the current size of the archive
600 *
601 * @return $size Size of the archive
602 */
603 public function getArchiveSize() {
604 if (!is_resource($this->zipFile)) {
605 return strlen($this->zipData);
606 }
607 $filestat = fstat($this->zipFile);
608
609 return $filestat['size'];
610 }
611
612 /**
613 * Calculate the 2 byte dostime used in the zip entries.
614 *
615 * @param int $timestamp
616 * @return 2-byte encoded DOS Date
617 */
618 private function getDosTime($timestamp = 0) {
619 $timestamp = (int)$timestamp;
620 $oldTZ = @date_default_timezone_get();
621 date_default_timezone_set('UTC');
622 $date = ($timestamp == 0 ? getdate() : getdate($timestamp));
623 date_default_timezone_set($oldTZ);
624 if ($date["year"] >= 1980) {
625 return pack("V", (($date["mday"] + ($date["mon"] << 5) + (($date["year"]-1980) << 9)) << 16) |
626 (($date["seconds"] >> 1) + ($date["minutes"] << 5) + ($date["hours"] << 11)));
627 }
628 return "\x00\x00\x00\x00";
629 }
630
631 /**
632 * Build the Zip file structures
633 *
634 * @param string $filePath
635 * @param string $fileComment
636 * @param string $gpFlags
637 * @param string $gzType
638 * @param int $timestamp
639 * @param string $fileCRC32
640 * @param int $gzLength
641 * @param int $dataLength
642 * @param int $extFileAttr Use self::EXT_FILE_ATTR_FILE for files, self::EXT_FILE_ATTR_DIR for Directories.
643 */
644 private function buildZipEntry($filePath, $fileComment, $gpFlags, $gzType, $timestamp, $fileCRC32, $gzLength, $dataLength, $extFileAttr) {
645 $filePath = str_replace("\\", "/", $filePath);
646 $fileCommentLength = (empty($fileComment) ? 0 : strlen($fileComment));
647 $timestamp = (int)$timestamp;
648 $timestamp = ($timestamp == 0 ? time() : $timestamp);
649
650 $dosTime = $this->getDosTime($timestamp);
651 $tsPack = pack("V", $timestamp);
652
653 $ux = "\x75\x78\x0B\x00\x01\x04\xE8\x03\x00\x00\x04\x00\x00\x00\x00";
654
655 if (!isset($gpFlags) || strlen($gpFlags) != 2) {
656 $gpFlags = "\x00\x00";
657 }
658
659 $isFileUTF8 = mb_check_encoding($filePath, "UTF-8") && !mb_check_encoding($filePath, "ASCII");
660 $isCommentUTF8 = !empty($fileComment) && mb_check_encoding($fileComment, "UTF-8") && !mb_check_encoding($fileComment, "ASCII");
661 if ($isFileUTF8 || $isCommentUTF8) {
662 $flag = 0;
663 $gpFlagsV = unpack("vflags", $gpFlags);
664 if (isset($gpFlagsV['flags'])) {
665 $flag = $gpFlagsV['flags'];
666 }
667 $gpFlags = pack("v", $flag | (1 << 11));
668 }
669
670 $header = $gpFlags . $gzType . $dosTime. $fileCRC32
671 . pack("VVv", $gzLength, $dataLength, strlen($filePath)); // File name length
672
673 $zipEntry = self::ZIP_LOCAL_FILE_HEADER;
674 $zipEntry .= self::ATTR_VERSION_TO_EXTRACT;
675 $zipEntry .= $header;
676 $zipEntry .= pack("v", ($this->addExtraField ? 28 : 0)); // Extra field length
677 $zipEntry .= $filePath; // FileName
678 // Extra fields
679 if ($this->addExtraField) {
680 $zipEntry .= "\x55\x54\x09\x00\x03" . $tsPack . $tsPack . $ux;
681 }
682 $this->zipwrite($zipEntry);
683
684 $cdEntry = self::ZIP_CENTRAL_FILE_HEADER;
685 $cdEntry .= self::ATTR_MADE_BY_VERSION;
686 $cdEntry .= ($dataLength === 0 ? "\x0A\x00" : self::ATTR_VERSION_TO_EXTRACT);
687 $cdEntry .= $header;
688 $cdEntry .= pack("v", ($this->addExtraField ? 24 : 0)); // Extra field length
689 $cdEntry .= pack("v", $fileCommentLength); // File comment length
690 $cdEntry .= "\x00\x00"; // Disk number start
691 $cdEntry .= "\x00\x00"; // internal file attributes
692 $cdEntry .= pack("V", $extFileAttr); // External file attributes
693 $cdEntry .= pack("V", $this->offset); // Relative offset of local header
694 $cdEntry .= $filePath; // FileName
695 // Extra fields
696 if ($this->addExtraField) {
697 $cdEntry .= "\x55\x54\x05\x00\x03" . $tsPack . $ux;
698 }
699 if (!empty($fileComment)) {
700 $cdEntry .= $fileComment; // Comment
701 }
702
703 $this->cdRec[] = $cdEntry;
704 $this->offset += strlen($zipEntry) + $gzLength;
705 }
706
707 private function zipwrite($data) {
708 if (!is_resource($this->zipFile)) {
709 $this->zipData .= $data;
710 } else {
711 fwrite($this->zipFile, $data);
712 fflush($this->zipFile);
713 }
714 }
715
716 private function zipflush() {
717 if (!is_resource($this->zipFile)) {
718 $this->zipFile = tmpfile();
719 fwrite($this->zipFile, $this->zipData);
720 $this->zipData = NULL;
721 }
722 }
723
724 /**
725 * Join $file to $dir path, and clean up any excess slashes.
726 *
727 * @param string $dir
728 * @param string $file
729 */
730 public static function pathJoin($dir, $file) {
731 if (empty($dir) || empty($file)) {
732 return self::getRelativePath($dir . $file);
733 }
734 return self::getRelativePath($dir . '/' . $file);
735 }
736
737 /**
738 * Clean up a path, removing any unnecessary elements such as /./, // or redundant ../ segments.
739 * If the path starts with a "/", it is deemed an absolute path and any /../ in the beginning is stripped off.
740 * The returned path will not end in a "/".
741 *
742 * Sometimes, when a path is generated from multiple fragments,
743 * you can get something like "../data/html/../images/image.jpeg"
744 * This will normalize that example path to "../data/images/image.jpeg"
745 *
746 * @param string $path The path to clean up
747 * @return string the clean path
748 */
749 public static function getRelativePath($path) {
750 $path = preg_replace("#/+\.?/+#", "/", str_replace("\\", "/", $path));
751 $dirs = explode("/", rtrim(preg_replace('#^(?:\./)+#', '', $path), '/'));
752
753 $offset = 0;
754 $sub = 0;
755 $subOffset = 0;
756 $root = "";
757
758 if (empty($dirs[0])) {
759 $root = "/";
760 $dirs = array_splice($dirs, 1);
761 } else if (preg_match("#[A-Za-z]:#", $dirs[0])) {
762 $root = strtoupper($dirs[0]) . "/";
763 $dirs = array_splice($dirs, 1);
764 }
765
766 $newDirs = array();
767 foreach ($dirs as $dir) {
768 if ($dir !== "..") {
769 $subOffset--;
770 $newDirs[++$offset] = $dir;
771 } else {
772 $subOffset++;
773 if (--$offset < 0) {
774 $offset = 0;
775 if ($subOffset > $sub) {
776 $sub++;
777 }
778 }
779 }
780 }
781
782 if (empty($root)) {
783 $root = str_repeat("../", $sub);
784 }
785 return $root . implode("/", array_slice($newDirs, 0, $offset));
786 }
787
788 /**
789 * Create the file permissions for a file or directory, for use in the extFileAttr parameters.
790 *
791 * @param int $owner Unix permisions for owner (octal from 00 to 07)
792 * @param int $group Unix permisions for group (octal from 00 to 07)
793 * @param int $other Unix permisions for others (octal from 00 to 07)
794 * @param bool $isFile
795 * @return EXTRERNAL_REF field.
796 */
797 public static function generateExtAttr($owner = 07, $group = 05, $other = 05, $isFile = true) {
798 $fp = $isFile ? self::S_IFREG : self::S_IFDIR;
799 $fp |= (($owner & 07) << 6) | (($group & 07) << 3) | ($other & 07);
800
801 return ($fp << 16) | ($isFile ? self::S_DOS_A : self::S_DOS_D);
802 }
803
804 /**
805 * Get the file permissions for a file or directory, for use in the extFileAttr parameters.
806 *
807 * @param string $filename
808 * @return external ref field, or FALSE if the file is not found.
809 */
810 public static function getFileExtAttr($filename) {
811 if (file_exists($filename)) {
812 $fp = fileperms($filename) << 16;
813 return $fp | (is_dir($filename) ? self::S_DOS_D : self::S_DOS_A);
814 }
815 return FALSE;
816 }
817}
818?>
diff --git a/inc/3rdparty/libraries/PHPePub/lib.uuid.LICENCE.txt b/inc/3rdparty/libraries/PHPePub/lib.uuid.LICENCE.txt
new file mode 100644
index 00000000..9424a83e
--- /dev/null
+++ b/inc/3rdparty/libraries/PHPePub/lib.uuid.LICENCE.txt
@@ -0,0 +1,31 @@
1 DrUUID RFC4122 library for PHP5
2 by J. King (http://jkingweb.ca/)
3 Licensed under MIT license
4
5 See http://jkingweb.ca/code/php/lib.uuid/
6 for documentation
7
8 Last revised 2010-02-15
9
10Copyright (c) 2009 J. King
11
12Permission is hereby granted, free of charge, to any person
13obtaining a copy of this software and associated documentation
14files (the "Software"), to deal in the Software without
15restriction, including without limitation the rights to use,
16copy, modify, merge, publish, distribute, sublicense, and/or sell
17copies of the Software, and to permit persons to whom the
18Software is furnished to do so, subject to the following
19conditions:
20
21The above copyright notice and this permission notice shall be
22included in all copies or substantial portions of the Software.
23
24THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
26OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
28HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
29WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
31OTHER DEALINGS IN THE SOFTWARE.
diff --git a/inc/3rdparty/libraries/PHPePub/lib.uuid.php b/inc/3rdparty/libraries/PHPePub/lib.uuid.php
new file mode 100644
index 00000000..c6a8de52
--- /dev/null
+++ b/inc/3rdparty/libraries/PHPePub/lib.uuid.php
@@ -0,0 +1,314 @@
1<?php
2/*
3 DrUUID RFC4122 library for PHP5
4by J. King (http://jkingweb.ca/)
5Licensed under MIT license
6
7See http://jkingweb.ca/code/php/lib.uuid/
8for documentation
9
10Last revised 2010-02-15
11*/
12
13/*
14 Copyright (c) 2009 J. King
15
16Permission is hereby granted, free of charge, to any person
17obtaining a copy of this software and associated documentation
18files (the "Software"), to deal in the Software without
19restriction, including without limitation the rights to use,
20copy, modify, merge, publish, distribute, sublicense, and/or sell
21copies of the Software, and to permit persons to whom the
22Software is furnished to do so, subject to the following
23conditions:
24
25The above copyright notice and this permission notice shall be
26included in all copies or substantial portions of the Software.
27
28THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
30OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
32HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
33WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
34FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
35OTHER DEALINGS IN THE SOFTWARE.
36*/
37
38
39class UUID {
40 const MD5 = 3;
41 const SHA1 = 5;
42 const clearVer = 15; // 00001111 Clears all bits of version byte with AND
43 const clearVar = 63; // 00111111 Clears all relevant bits of variant byte with AND
44 const varRes = 224; // 11100000 Variant reserved for future use
45 const varMS = 192; // 11000000 Microsft GUID variant
46 const varRFC = 128; // 10000000 The RFC 4122 variant (this variant)
47 const varNCS = 0; // 00000000 The NCS compatibility variant
48 const version1 = 16; // 00010000
49 const version3 = 48; // 00110000
50 const version4 = 64; // 01000000
51 const version5 = 80; // 01010000
52 const interval = 0x01b21dd213814000; // Time (in 100ns steps) between the start of the UTC and Unix epochs
53 const nsDNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
54 const nsURL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
55 const nsOID = '6ba7b812-9dad-11d1-80b4-00c04fd430c8';
56 const nsX500 = '6ba7b814-9dad-11d1-80b4-00c04fd430c8';
57 protected static $randomFunc = 'randomTwister';
58 protected static $randomSource = NULL;
59 //instance properties
60 protected $bytes;
61 protected $hex;
62 protected $string;
63 protected $urn;
64 protected $version;
65 protected $variant;
66 protected $node;
67 protected $time;
68
69 public static function mint($ver = 1, $node = NULL, $ns = NULL) {
70 /* Create a new UUID based on provided data. */
71 switch((int) $ver) {
72 case 1:
73 return new self(self::mintTime($node));
74 case 2:
75 // Version 2 is not supported
76 throw new UUIDException("Version 2 is unsupported.");
77 case 3:
78 return new self(self::mintName(self::MD5, $node, $ns));
79 case 4:
80 return new self(self::mintRand());
81 case 5:
82 return new self(self::mintName(self::SHA1, $node, $ns));
83 default:
84 throw new UUIDException("Selected version is invalid or unsupported.");
85 }
86 }
87
88 public static function import($uuid) {
89 /* Import an existing UUID. */
90 return new self(self::makeBin($uuid, 16));
91 }
92
93 public static function compare($a, $b) {
94 /* Compares the binary representations of two UUIDs.
95 The comparison will return true if they are bit-exact,
96 or if neither is valid. */
97 if (self::makeBin($a, 16)==self::makeBin($b, 16)) {
98 return TRUE;
99 } else {
100 return FALSE;
101 }
102 }
103
104 public function __toString() {
105 return $this->string;
106 }
107
108 public function __get($var) {
109 switch($var) {
110 case "bytes":
111 return $this->bytes;
112 case "hex":
113 return bin2hex($this->bytes);
114 case "string":
115 return $this->__toString();
116 case "urn":
117 return "urn:uuid:".$this->__toString();
118 case "version":
119 return ord($this->bytes[6]) >> 4;
120 case "variant":
121 $byte = ord($this->bytes[8]);
122 if ($byte >= self::varRes) {
123 return 3;
124 }
125 if ($byte >= self::varMS) {
126 return 2;
127 }
128 if ($byte >= self::varRFC) {
129 return 1;
130 }
131 return 0;
132 case "node":
133 if (ord($this->bytes[6])>>4==1) {
134 return bin2hex(substr($this->bytes,10));
135 } else {
136 return NULL;
137 }
138 case "time":
139 if (ord($this->bytes[6])>>4==1) {
140 // Restore contiguous big-endian byte order
141 $time = bin2hex($this->bytes[6].$this->bytes[7].$this->bytes[4].$this->bytes[5].$this->bytes[0].$this->bytes[1].$this->bytes[2].$this->bytes[3]);
142 // Clear version flag
143 $time[0] = "0";
144 // Do some reverse arithmetic to get a Unix timestamp
145 $time = (hexdec($time) - self::interval) / 10000000;
146 return $time;
147 } else {
148 return NULL;
149 }
150 default:
151 return NULL;
152 }
153 }
154
155 protected function __construct($uuid) {
156 if (strlen($uuid) != 16) {
157 throw new UUIDException("Input must be a 128-bit integer.");
158 }
159 $this->bytes = $uuid;
160 // Optimize the most common use
161 $this->string =
162 bin2hex(substr($uuid,0,4))."-".
163 bin2hex(substr($uuid,4,2))."-".
164 bin2hex(substr($uuid,6,2))."-".
165 bin2hex(substr($uuid,8,2))."-".
166 bin2hex(substr($uuid,10,6));
167 }
168
169 protected static function mintTime($node = NULL) {
170 /* Generates a Version 1 UUID.
171 These are derived from the time at which they were generated. */
172 // Get time since Gregorian calendar reform in 100ns intervals
173 // This is exceedingly difficult because of PHP's (and pack()'s)
174 // integer size limits.
175 // Note that this will never be more accurate than to the microsecond.
176 $time = microtime(1) * 10000000 + self::interval;
177 // Convert to a string representation
178 $time = sprintf("%F", $time);
179 preg_match("/^\d+/", $time, $time); //strip decimal point
180 // And now to a 64-bit binary representation
181 $time = base_convert($time[0], 10, 16);
182 $time = pack("H*", str_pad($time, 16, "0", STR_PAD_LEFT));
183 // Reorder bytes to their proper locations in the UUID
184 $uuid = $time[4].$time[5].$time[6].$time[7].$time[2].$time[3].$time[0].$time[1];
185 // Generate a random clock sequence
186 $uuid .= self::randomBytes(2);
187 // set variant
188 $uuid[8] = chr(ord($uuid[8]) & self::clearVar | self::varRFC);
189 // set version
190 $uuid[6] = chr(ord($uuid[6]) & self::clearVer | self::version1);
191 // Set the final 'node' parameter, a MAC address
192 if ($node) {
193 $node = self::makeBin($node, 6);
194 }
195 if (!$node) {
196 // If no node was provided or if the node was invalid,
197 // generate a random MAC address and set the multicast bit
198 $node = self::randomBytes(6);
199 $node[0] = pack("C", ord($node[0]) | 1);
200 }
201 $uuid .= $node;
202 return $uuid;
203 }
204
205 protected static function mintRand() {
206 /* Generate a Version 4 UUID.
207 These are derived soly from random numbers. */
208 // generate random fields
209 $uuid = self::randomBytes(16);
210 // set variant
211 $uuid[8] = chr(ord($uuid[8]) & self::clearVar | self::varRFC);
212 // set version
213 $uuid[6] = chr(ord($uuid[6]) & self::clearVer | self::version4);
214 return $uuid;
215 }
216
217 protected static function mintName($ver, $node, $ns) {
218 /* Generates a Version 3 or Version 5 UUID.
219 These are derived from a hash of a name and its namespace, in binary form. */
220 if (!$node) {
221 throw new UUIDException("A name-string is required for Version 3 or 5 UUIDs.");
222 }
223 // if the namespace UUID isn't binary, make it so
224 $ns = self::makeBin($ns, 16);
225 if (!$ns) {
226 throw new UUIDException("A binary namespace is required for Version 3 or 5 UUIDs.");
227 }
228 $uuid = null;
229 $version = self::version3;
230 switch($ver) {
231 case self::MD5:
232 $version = self::version3;
233 $uuid = md5($ns.$node,1);
234 break;
235 case self::SHA1:
236 $version = self::version5;
237 $uuid = substr(sha1($ns.$node,1),0, 16);
238 break;
239 }
240 // set variant
241 $uuid[8] = chr(ord($uuid[8]) & self::clearVar | self::varRFC);
242 // set version
243 $uuid[6] = chr(ord($uuid[6]) & self::clearVer | $version);
244 return ($uuid);
245 }
246
247 protected static function makeBin($str, $len) {
248 /* Insure that an input string is either binary or hexadecimal.
249 Returns binary representation, or false on failure. */
250 if ($str instanceof self) {
251 return $str->bytes;
252 }
253 if (strlen($str)==$len) {
254 return $str;
255 } else {
256 $str = preg_replace("/^urn:uuid:/is", "", $str); // strip URN scheme and namespace
257 }
258 $str = preg_replace("/[^a-f0-9]/is", "", $str); // strip non-hex characters
259 if (strlen($str) != ($len * 2)) {
260 return FALSE;
261 } else {
262 return pack("H*", $str);
263 }
264 }
265
266 public static function initRandom() {
267 /* Look for a system-provided source of randomness, which is usually crytographically secure.
268 /dev/urandom is tried first simply out of bias for Linux systems. */
269 if (is_readable('/dev/urandom')) {
270 self::$randomSource = fopen('/dev/urandom', 'rb');
271 self::$randomFunc = 'randomFRead';
272 }
273 else if (class_exists('COM', 0)) {
274 try {
275 self::$randomSource = new COM('CAPICOM.Utilities.1'); // See http://msdn.microsoft.com/en-us/library/aa388182(VS.85).aspx
276 self::$randomFunc = 'randomCOM';
277 }
278 catch(Exception $e) {
279 }
280 }
281 return self::$randomFunc;
282 }
283
284 public static function randomBytes($bytes) {
285 return call_user_func(array('self', self::$randomFunc), $bytes);
286 }
287
288 protected static function randomTwister($bytes) {
289 /* Get the specified number of random bytes, using mt_rand().
290 Randomness is returned as a string of bytes. */
291 $rand = "";
292 for ($a = 0; $a < $bytes; $a++) {
293 $rand .= chr(mt_rand(0, 255));
294 }
295 return $rand;
296 }
297
298 protected static function randomFRead($bytes) {
299 /* Get the specified number of random bytes using a file handle
300 previously opened with UUID::initRandom().
301 Randomness is returned as a string of bytes. */
302 return fread(self::$randomSource, $bytes);
303 }
304
305 protected static function randomCOM($bytes) {
306 /* Get the specified number of random bytes using Windows'
307 randomness source via a COM object previously created by UUID::initRandom().
308 Randomness is returned as a string of bytes. */
309 return base64_decode(self::$randomSource->GetRandom($bytes,0)); // straight binary mysteriously doesn't work, hence the base64
310 }
311}
312
313class UUIDException extends Exception {
314}
diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php
index 7e3e6afe..e5539468 100755
--- a/inc/poche/Poche.class.php
+++ b/inc/poche/Poche.class.php
@@ -1131,4 +1131,87 @@ class Poche
1131 1131
1132 return new HTMLPurifier($config); 1132 return new HTMLPurifier($config);
1133 } 1133 }
1134
1135 /**
1136 * handle epub
1137 */
1138 public function createEpub() {
1139
1140 if (isset($_GET['epub']) && isset($_GET['id'])) {
1141 if ($_GET['id'] == "all") { // we put all entries in the file
1142 $entries = $this->store->retrieveAll($this->user->getId());
1143 }
1144 else { // we put only one entry in the file
1145 $entryID = filter_var($_GET['id'],FILTER_SANITIZE_NUMBER_INT);
1146 $entry = $this->store->retrieveOneById($entryID, $this->user->getId());
1147 $entries = array($entry);
1148 }
1149 }
1150 $content_start =
1151 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
1152 . "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n"
1153 . " \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
1154 . "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1155 . "<head>"
1156 . "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n"
1157 . "<link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\" />\n"
1158 . "<title>Test Book</title>\n"
1159 . "</head>\n"
1160 . "<body>\n";
1161
1162 $bookEnd = "</body>\n</html>\n";
1163
1164 $log = new Logger($entryID, TRUE);
1165 $fileDir = CACHE;
1166
1167
1168 $book = new EPub();
1169 $log->logLine("new EPub()");
1170 $log->logLine("EPub class version: " . EPub::VERSION);
1171 $log->logLine("EPub Req. Zip version: " . EPub::REQ_ZIP_VERSION);
1172 $log->logLine("Zip version: " . Zip::VERSION);
1173 $log->logLine("getCurrentServerURL: " . $book->getCurrentServerURL());
1174 $log->logLine("getCurrentPageURL..: " . $book->getCurrentPageURL());
1175
1176 $book->setTitle("wallabag's articles");
1177 $book->setIdentifier("http://$_SERVER[HTTP_HOST]", EPub::IDENTIFIER_URI); // Could also be the ISBN number, prefered for published books, or a UUID.
1178 //$book->setLanguage("en"); // Not needed, but included for the example, Language is mandatory, but EPub defaults to "en". Use RFC3066 Language codes, such as "en", "da", "fr" etc.
1179 $book->setDescription("Some articles saved on my wallabag");
1180 $book->setAuthor("wallabag","wallabag");
1181 $book->setPublisher("wallabag","wallabag"); // I hope this is a non existant address :)
1182 $book->setDate(time()); // Strictly not needed as the book date defaults to time().
1183 //$book->setRights("Copyright and licence information specific for the book."); // As this is generated, this _could_ contain the name or licence information of the user who purchased the book, if needed. If this is used that way, the identifier must also be made unique for the book.
1184 $book->setSourceURL("http://$_SERVER[HTTP_HOST]");
1185
1186 $book->addDublinCoreMetadata(DublinCore::CONTRIBUTOR, "PHP");
1187
1188 $cssData = "body {\n margin-left: .5em;\n margin-right: .5em;\n text-align: justify;\n}\n\np {\n font-family: serif;\n font-size: 10pt;\n text-align: justify;\n text-indent: 1em;\n margin-top: 0px;\n margin-bottom: 1ex;\n}\n\nh1, h2 {\n font-family: sans-serif;\n font-style: italic;\n text-align: center;\n background-color: #6b879c;\n color: white;\n width: 100%;\n}\n\nh1 {\n margin-bottom: 2px;\n}\n\nh2 {\n margin-top: -2px;\n margin-bottom: 2px;\n}\n";
1189 $cover = $content_start . "<h1>My articles on wallabag</h1>\n<h2>As seen on : http://$_SERVER[HTTP_HOST]</h2>\n" . $bookEnd;
1190 $book->addChapter("Notices", "Cover.html", $cover);
1191 $book->buildTOC(NULL, "toc", "Table of Contents", TRUE, TRUE);
1192
1193 foreach ($entries as $entry) {
1194 $tags = $this->store->retrieveTagsByEntry($entry['id']);
1195 foreach ($tags as $tag) {
1196 $book->setSubject($tag);
1197 }
1198
1199 $log->logLine("Set up parameters");
1200
1201
1202
1203 $chapter = $content_start . $entry['content'] . $bookEnd;
1204 $book->addChapter("Chapter " . $entry['id'] . ": " . $entry['title'], htmlspecialchars($entry['title']) . ".html", $chapter, true, EPub::EXTERNAL_REF_ADD);
1205 }
1206
1207
1208 if (true) {
1209 $epuplog = $book->getLog();
1210 $book->addChapter("Log", "Log.html", $content_start . $log->getLog() . "\n</pre>" . $bookEnd); // generation log
1211 // Only used in case we need to debug EPub.php.
1212 //$book->addChapter("ePubLog", "ePubLog.html", $content_start . $epuplog . "\n</pre>" . $bookEnd);
1213 }
1214 $book->finalize();
1215 $zipData = $book->sendBook("wallabag's articles");
1216 }
1134} 1217}
diff --git a/inc/poche/global.inc.php b/inc/poche/global.inc.php
index 14e9dd93..8cf86d03 100755
--- a/inc/poche/global.inc.php
+++ b/inc/poche/global.inc.php
@@ -31,6 +31,11 @@ require_once INCLUDES . '/3rdparty/FlattrItem.class.php';
31 31
32require_once INCLUDES . '/3rdparty/htmlpurifier/HTMLPurifier.auto.php'; 32require_once INCLUDES . '/3rdparty/htmlpurifier/HTMLPurifier.auto.php';
33 33
34# epub library
35require_once INCLUDES . '/3rdparty/libraries/PHPePub/Logger.php';
36require_once INCLUDES . '/3rdparty/libraries/PHPePub/EPub.php';
37require_once INCLUDES . '/3rdparty/libraries/PHPePub/EPubChapterSplitter.php';
38
34# Composer its autoloader for automatically loading Twig 39# Composer its autoloader for automatically loading Twig
35if (! file_exists(ROOT . '/vendor/autoload.php')) { 40if (! file_exists(ROOT . '/vendor/autoload.php')) {
36 Poche::$canRenderTemplates = false; 41 Poche::$canRenderTemplates = false;
diff --git a/index.php b/index.php
index 9c943b1d..79838ed9 100755
--- a/index.php
+++ b/index.php
@@ -70,6 +70,8 @@ if (isset($_GET['login'])) {
70 $poche->createNewUser(); 70 $poche->createNewUser();
71} elseif (isset($_GET['deluser'])) { 71} elseif (isset($_GET['deluser'])) {
72 $poche->deleteUser(); 72 $poche->deleteUser();
73} elseif (isset($_GET['epub'])) {
74 $poche->createEpub();
73} elseif (isset($_GET['import'])) { 75} elseif (isset($_GET['import'])) {
74 $import = $poche->import(); 76 $import = $poche->import();
75 $tpl_vars = array_merge($tpl_vars, $import); 77 $tpl_vars = array_merge($tpl_vars, $import);
diff --git a/themes/baggy/config.twig b/themes/baggy/config.twig
index 29d9e048..f43f6d01 100755
--- a/themes/baggy/config.twig
+++ b/themes/baggy/config.twig
@@ -124,6 +124,9 @@
124 {% if constant('STORAGE') == 'sqlite' %} 124 {% if constant('STORAGE') == 'sqlite' %}
125 <p><a href="?download" target="_blank">{% trans "Click here" %}</a> {% trans "to download your database." %}</p>{% endif %} 125 <p><a href="?download" target="_blank">{% trans "Click here" %}</a> {% trans "to download your database." %}</p>{% endif %}
126 <p><a href="?export" target="_blank">{% trans "Click here" %}</a> {% trans "to export your wallabag data." %}</p> 126 <p><a href="?export" target="_blank">{% trans "Click here" %}</a> {% trans "to export your wallabag data." %}</p>
127
128 <h2>Fancy a ebook ?</h2>
129 Click on <a href="./?epub&amp;id=all" title="Generate ePub">this link</a> to get all your articles in one ebook (ePub).
127 130
128 <h2>{% trans "Cache" %}</h2> 131 <h2>{% trans "Cache" %}</h2>
129 <p><a href="?empty-cache">{% trans "Click here" %}</a> {% trans "to delete cache." %}</p> 132 <p><a href="?empty-cache">{% trans "Click here" %}</a> {% trans "to delete cache." %}</p>
diff --git a/themes/baggy/view.twig b/themes/baggy/view.twig
index 62af2516..0dff4e27 100755
--- a/themes/baggy/view.twig
+++ b/themes/baggy/view.twig
@@ -16,6 +16,7 @@
16 {% if constant('SHARE_SHAARLI') == 1 %}<li><a href="{{ constant('SHAARLI_URL') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}" target="_blank" class="tool shaarli" title="{% trans "shaarli" %}"><span>{% trans "shaarli" %}</span></a></li>{% endif %} 16 {% if constant('SHARE_SHAARLI') == 1 %}<li><a href="{{ constant('SHAARLI_URL') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}" target="_blank" class="tool shaarli" title="{% trans "shaarli" %}"><span>{% trans "shaarli" %}</span></a></li>{% endif %}
17 {% if constant('FLATTR') == 1 %}{% if flattr.status == constant('FLATTRABLE') %}<li><a href="http://flattr.com/submit/auto?url={{ entry.url }}" class="tool flattr icon icon-flattr" target="_blank" title="{% trans "flattr" %}"><span>{% trans "flattr" %}</span></a></li>{% elseif flattr.status == constant('FLATTRED') %}<li><a href="{{ flattr.flattrItemURL }}" class="tool flattr icon icon-flattr" target="_blank" title="{% trans "flattr" %}"><span>{% trans "flattr" %}</span> ({{ flattr.numflattrs }})</a></li>{% endif %}{% endif %} 17 {% if constant('FLATTR') == 1 %}{% if flattr.status == constant('FLATTRABLE') %}<li><a href="http://flattr.com/submit/auto?url={{ entry.url }}" class="tool flattr icon icon-flattr" target="_blank" title="{% trans "flattr" %}"><span>{% trans "flattr" %}</span></a></li>{% elseif flattr.status == constant('FLATTRED') %}<li><a href="{{ flattr.flattrItemURL }}" class="tool flattr icon icon-flattr" target="_blank" title="{% trans "flattr" %}"><span>{% trans "flattr" %}</span> ({{ flattr.numflattrs }})</a></li>{% endif %}{% endif %}
18 {% if constant('SHOW_PRINTLINK') == 1 %}<li><a title="{% trans "Print" %}" class="tool icon icon-print" href="javascript: window.print();"><span>{% trans "Print" %}</span></a></li>{% endif %} 18 {% if constant('SHOW_PRINTLINK') == 1 %}<li><a title="{% trans "Print" %}" class="tool icon icon-print" href="javascript: window.print();"><span>{% trans "Print" %}</span></a></li>{% endif %}
19 <li><a href="./?epub&amp;id={{ entry.id|e }}" title="Generate epub file">EPUB</a></li>
19 <li><a href="mailto:hello@wallabag.org?subject=Wrong%20display%20in%20wallabag&amp;body={{ entry.url|url_encode }}" title="{% trans "Does this article appear wrong?" %}" class="tool bad-display icon icon-delete"><span>{% trans "Does this article appear wrong?" %}</span></a></li> 20 <li><a href="mailto:hello@wallabag.org?subject=Wrong%20display%20in%20wallabag&amp;body={{ entry.url|url_encode }}" title="{% trans "Does this article appear wrong?" %}" class="tool bad-display icon icon-delete"><span>{% trans "Does this article appear wrong?" %}</span></a></li>
20 </ul> 21 </ul>
21 </div> 22 </div>