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