diff options
Diffstat (limited to 'inc/3rdparty/libraries/PHPePub')
-rw-r--r-- | inc/3rdparty/libraries/PHPePub/EPub.HtmlEntities.php | 266 | ||||
-rw-r--r-- | inc/3rdparty/libraries/PHPePub/EPub.NCX.php | 782 | ||||
-rw-r--r-- | inc/3rdparty/libraries/PHPePub/EPub.OPF.php | 1226 | ||||
-rw-r--r-- | inc/3rdparty/libraries/PHPePub/EPub.php | 2429 | ||||
-rw-r--r-- | inc/3rdparty/libraries/PHPePub/EPubChapterSplitter.php | 201 | ||||
-rw-r--r-- | inc/3rdparty/libraries/PHPePub/Logger.php | 92 | ||||
-rw-r--r-- | inc/3rdparty/libraries/PHPePub/Zip.php | 818 | ||||
-rw-r--r-- | inc/3rdparty/libraries/PHPePub/lib.uuid.LICENCE.txt | 31 | ||||
-rw-r--r-- | inc/3rdparty/libraries/PHPePub/lib.uuid.php | 314 |
9 files changed, 6159 insertions, 0 deletions
diff --git a/inc/3rdparty/libraries/PHPePub/EPub.HtmlEntities.php b/inc/3rdparty/libraries/PHPePub/EPub.HtmlEntities.php new file mode 100644 index 00000000..376b6133 --- /dev/null +++ b/inc/3rdparty/libraries/PHPePub/EPub.HtmlEntities.php | |||
@@ -0,0 +1,266 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * This should be a complete list of all HTML entities, mapped to their UTF-8 character codes. | ||
4 | * | ||
5 | * @author A. Grandt | ||
6 | * @copyright A. Grandt 2009-2013 | ||
7 | * @license GNU LGPL, Attribution required for commercial implementations, requested for everything else. | ||
8 | * @version 3.00 | ||
9 | */ | ||
10 | global $htmlEntities; | ||
11 | $htmlEntities = array(); | ||
12 | |||
13 | $htmlEntities["""] ="\x22"; // " ((double) quotation mark) | ||
14 | $htmlEntities["&"] ="\x26"; // & (ampersand) | ||
15 | $htmlEntities["'"] ="\x27"; // ' (apostrophe = apostrophe-quote) | ||
16 | $htmlEntities["<"] ="\x3C"; // < (less-than sign) | ||
17 | $htmlEntities[">"] ="\x3E"; // > (greater-than sign) | ||
18 | $htmlEntities[" "] ="\xC2\xA0"; //   (non-breaking space) | ||
19 | $htmlEntities["¡"] ="\xC2\xA1"; // ¡ (inverted exclamation mark) | ||
20 | $htmlEntities["¢"] ="\xC2\xA2"; // ¢ (cent) | ||
21 | $htmlEntities["£"] ="\xC2\xA3"; // £ (pound) | ||
22 | $htmlEntities["¤"] ="\xC2\xA4"; // ¤ (currency) | ||
23 | $htmlEntities["¥"] ="\xC2\xA5"; // ¥ (yen) | ||
24 | $htmlEntities["¦"] ="\xC2\xA6"; // ¦ (broken vertical bar) | ||
25 | $htmlEntities["§"] ="\xC2\xA7"; // § (section) | ||
26 | $htmlEntities["¨"] ="\xC2\xA8"; // ¨ (spacing diaeresis) | ||
27 | $htmlEntities["©"] ="\xC2\xA9"; // © (copyright) | ||
28 | $htmlEntities["ª"] ="\xC2\xAA"; // ª (feminine ordinal indicator) | ||
29 | $htmlEntities["«"] ="\xC2\xAB"; // « (angle quotation mark (left)) | ||
30 | $htmlEntities["¬"] ="\xC2\xAC"; // ¬ (negation) | ||
31 | $htmlEntities["­"] ="\xC2\xAD"; // ­ (soft hyphen) | ||
32 | $htmlEntities["®"] ="\xC2\xAE"; // ® (registered trademark) | ||
33 | $htmlEntities["¯"] ="\xC2\xAF"; // ¯ (spacing macron) | ||
34 | $htmlEntities["°"] ="\xC2\xB0"; // ° (degree) | ||
35 | $htmlEntities["±"] ="\xC2\xB1"; // ± (plus-or-minus) | ||
36 | $htmlEntities["²"] ="\xC2\xB2"; // ² (superscript 2) | ||
37 | $htmlEntities["³"] ="\xC2\xB3"; // ³ (superscript 3) | ||
38 | $htmlEntities["´"] ="\xC2\xB4"; // ´ (spacing acute) | ||
39 | $htmlEntities["µ"] ="\xC2\xB5"; // µ (micro) | ||
40 | $htmlEntities["¶"] ="\xC2\xB6"; // ¶ (paragraph) | ||
41 | $htmlEntities["·"] ="\xC2\xB7"; // · (middle dot) | ||
42 | $htmlEntities["¸"] ="\xC2\xB8"; // ¸ (spacing cedilla) | ||
43 | $htmlEntities["¹"] ="\xC2\xB9"; // ¹ (superscript 1) | ||
44 | $htmlEntities["º"] ="\xC2\xBA"; // º (masculine ordinal indicator) | ||
45 | $htmlEntities["»"] ="\xC2\xBB"; // » (angle quotation mark (right)) | ||
46 | $htmlEntities["¼"] ="\xC2\xBC"; // ¼ (fraction 1/4) | ||
47 | $htmlEntities["½"] ="\xC2\xBD"; // ½ (fraction 1/2) | ||
48 | $htmlEntities["¾"] ="\xC2\xBE"; // ¾ (fraction 3/4) | ||
49 | $htmlEntities["¿"] ="\xC2\xBF"; // ¿ (inverted question mark) | ||
50 | $htmlEntities["À"] ="\xC3\x80"; // À (capital a, grave accent) | ||
51 | $htmlEntities["Á"] ="\xC3\x81"; // Á (capital a, acute accent) | ||
52 | $htmlEntities["Â"] ="\xC3\x82"; // Â (capital a, circumflex accent) | ||
53 | $htmlEntities["Ã"] ="\xC3\x83"; // Ã (capital a, tilde) | ||
54 | $htmlEntities["Ä"] ="\xC3\x84"; // Ä (capital a, umlaut mark) | ||
55 | $htmlEntities["Å"] ="\xC3\x85"; // Å (capital a, ring) | ||
56 | $htmlEntities["Æ"] ="\xC3\x86"; // Æ (capital ae) | ||
57 | $htmlEntities["Ç"] ="\xC3\x87"; // Ç (capital c, cedilla) | ||
58 | $htmlEntities["È"] ="\xC3\x88"; // È (capital e, grave accent) | ||
59 | $htmlEntities["É"] ="\xC3\x89"; // É (capital e, acute accent) | ||
60 | $htmlEntities["Ê"] ="\xC3\x8A"; // Ê (capital e, circumflex accent) | ||
61 | $htmlEntities["Ë"] ="\xC3\x8B"; // Ë (capital e, umlaut mark) | ||
62 | $htmlEntities["Ì"] ="\xC3\x8C"; // Ì (capital i, grave accent) | ||
63 | $htmlEntities["Í"] ="\xC3\x8D"; // Í (capital i, acute accent) | ||
64 | $htmlEntities["Î"] ="\xC3\x8E"; // Î (capital i, circumflex accent) | ||
65 | $htmlEntities["Ï"] ="\xC3\x8F"; // Ï (capital i, umlaut mark) | ||
66 | $htmlEntities["Ð"] ="\xC3\x90"; // Ð (capital eth, Icelandic) | ||
67 | $htmlEntities["Ñ"] ="\xC3\x91"; // Ñ (capital n, tilde) | ||
68 | $htmlEntities["Ò"] ="\xC3\x92"; // Ò (capital o, grave accent) | ||
69 | $htmlEntities["Ó"] ="\xC3\x93"; // Ó (capital o, acute accent) | ||
70 | $htmlEntities["Ô"] ="\xC3\x94"; // Ô (capital o, circumflex accent) | ||
71 | $htmlEntities["Õ"] ="\xC3\x95"; // Õ (capital o, tilde) | ||
72 | $htmlEntities["Ö"] ="\xC3\x96"; // Ö (capital o, umlaut mark) | ||
73 | $htmlEntities["×"] ="\xC3\x97"; // × (multiplication) | ||
74 | $htmlEntities["Ø"] ="\xC3\x98"; // Ø (capital o, slash) | ||
75 | $htmlEntities["Ù"] ="\xC3\x99"; // Ù (capital u, grave accent) | ||
76 | $htmlEntities["Ú"] ="\xC3\x9A"; // Ú (capital u, acute accent) | ||
77 | $htmlEntities["Û"] ="\xC3\x9B"; // Û (capital u, circumflex accent) | ||
78 | $htmlEntities["Ü"] ="\xC3\x9C"; // Ü (capital u, umlaut mark) | ||
79 | $htmlEntities["Ý"] ="\xC3\x9D"; // Ý (capital y, acute accent) | ||
80 | $htmlEntities["Þ"] ="\xC3\x9E"; // Þ (capital THORN, Icelandic) | ||
81 | $htmlEntities["ß"] ="\xC3\x9F"; // ß (small sharp s, German) | ||
82 | $htmlEntities["à"] ="\xC3\xA0"; // à (small a, grave accent) | ||
83 | $htmlEntities["á"] ="\xC3\xA1"; // á (small a, acute accent) | ||
84 | $htmlEntities["â"] ="\xC3\xA2"; // â (small a, circumflex accent) | ||
85 | $htmlEntities["ã"] ="\xC3\xA3"; // ã (small a, tilde) | ||
86 | $htmlEntities["ä"] ="\xC3\xA4"; // ä (small a, umlaut mark) | ||
87 | $htmlEntities["å"] ="\xC3\xA5"; // å (small a, ring) | ||
88 | $htmlEntities["æ"] ="\xC3\xA6"; // æ (small ae) | ||
89 | $htmlEntities["ç"] ="\xC3\xA7"; // ç (small c, cedilla) | ||
90 | $htmlEntities["è"] ="\xC3\xA8"; // è (small e, grave accent) | ||
91 | $htmlEntities["é"] ="\xC3\xA9"; // é (small e, acute accent) | ||
92 | $htmlEntities["ê"] ="\xC3\xAA"; // ê (small e, circumflex accent) | ||
93 | $htmlEntities["ë"] ="\xC3\xAB"; // ë (small e, umlaut mark) | ||
94 | $htmlEntities["ì"] ="\xC3\xAC"; // ì (small i, grave accent) | ||
95 | $htmlEntities["í"] ="\xC3\xAD"; // í (small i, acute accent) | ||
96 | $htmlEntities["î"] ="\xC3\xAE"; // î (small i, circumflex accent) | ||
97 | $htmlEntities["ï"] ="\xC3\xAF"; // ï (small i, umlaut mark) | ||
98 | $htmlEntities["ð"] ="\xC3\xB0"; // ð (small eth, Icelandic) | ||
99 | $htmlEntities["ñ"] ="\xC3\xB1"; // ñ (small n, tilde) | ||
100 | $htmlEntities["ò"] ="\xC3\xB2"; // ò (small o, grave accent) | ||
101 | $htmlEntities["ó"] ="\xC3\xB3"; // ó (small o, acute accent) | ||
102 | $htmlEntities["ô"] ="\xC3\xB4"; // ô (small o, circumflex accent) | ||
103 | $htmlEntities["õ"] ="\xC3\xB5"; // õ (small o, tilde) | ||
104 | $htmlEntities["ö"] ="\xC3\xB6"; // ö (small o, umlaut mark) | ||
105 | $htmlEntities["÷"] ="\xC3\xB7"; // ÷ (division) | ||
106 | $htmlEntities["ø"] ="\xC3\xB8"; // ø (small o, slash) | ||
107 | $htmlEntities["ù"] ="\xC3\xB9"; // ù (small u, grave accent) | ||
108 | $htmlEntities["ú"] ="\xC3\xBA"; // ú (small u, acute accent) | ||
109 | $htmlEntities["û"] ="\xC3\xBB"; // û (small u, circumflex accent) | ||
110 | $htmlEntities["ü"] ="\xC3\xBC"; // ü (small u, umlaut mark) | ||
111 | $htmlEntities["ý"] ="\xC3\xBD"; // ý (small y, acute accent) | ||
112 | $htmlEntities["þ"] ="\xC3\xBE"; // þ (small thorn, Icelandic) | ||
113 | $htmlEntities["ÿ"] ="\xC3\xBF"; // ÿ (small y, umlaut mark) | ||
114 | $htmlEntities["Œ"] ="\xC5\x92"; // Œ (capital ligature OE) | ||
115 | $htmlEntities["œ"] ="\xC5\x93"; // œ (small ligature oe) | ||
116 | $htmlEntities["Š"] ="\xC5\xA0"; // Š (capital S with caron) | ||
117 | $htmlEntities["š"] ="\xC5\xA1"; // š (small S with caron) | ||
118 | $htmlEntities["Ÿ"] ="\xC5\xB8"; // Ÿ (capital Y with diaeres) | ||
119 | $htmlEntities["ƒ"] ="\xC6\x92"; // ƒ (f with hook) | ||
120 | $htmlEntities["ˆ"] ="\xCB\x86"; // ˆ (modifier letter circumflex accent) | ||
121 | $htmlEntities["˜"] ="\xCB\x9C"; // ˜ (small tilde) | ||
122 | $htmlEntities["Α"] ="\xCE\x91"; // Α (Alpha) | ||
123 | $htmlEntities["Β"] ="\xCE\x92"; // Β (Beta) | ||
124 | $htmlEntities["Γ"] ="\xCE\x93"; // Γ (Gamma) | ||
125 | $htmlEntities["Δ"] ="\xCE\x94"; // Δ (Delta) | ||
126 | $htmlEntities["Ε"] ="\xCE\x95"; // Ε (Epsilon) | ||
127 | $htmlEntities["Ζ"] ="\xCE\x96"; // Ζ (Zeta) | ||
128 | $htmlEntities["Η"] ="\xCE\x97"; // Η (Eta) | ||
129 | $htmlEntities["Θ"] ="\xCE\x98"; // Θ (Theta) | ||
130 | $htmlEntities["Ι"] ="\xCE\x99"; // Ι (Iota) | ||
131 | $htmlEntities["Κ"] ="\xCE\x9A"; // Κ (Kappa) | ||
132 | $htmlEntities["Λ"] ="\xCE\x9B"; // Λ (Lambda) | ||
133 | $htmlEntities["Μ"] ="\xCE\x9C"; // Μ (Mu) | ||
134 | $htmlEntities["Ν"] ="\xCE\x9D"; // Ν (Nu) | ||
135 | $htmlEntities["Ξ"] ="\xCE\x9E"; // Ξ (Xi) | ||
136 | $htmlEntities["Ο"] ="\xCE\x9F"; // Ο (Omicron) | ||
137 | $htmlEntities["Π"] ="\xCE\xA0"; // Π (Pi) | ||
138 | $htmlEntities["Ρ"] ="\xCE\xA1"; // Ρ (Rho) | ||
139 | $htmlEntities["Σ"] ="\xCE\xA3"; // Σ (Sigma) | ||
140 | $htmlEntities["Τ"] ="\xCE\xA4"; // Τ (Tau) | ||
141 | $htmlEntities["Υ"] ="\xCE\xA5"; // Υ (Upsilon) | ||
142 | $htmlEntities["Φ"] ="\xCE\xA6"; // Φ (Phi) | ||
143 | $htmlEntities["Χ"] ="\xCE\xA7"; // Χ (Chi) | ||
144 | $htmlEntities["Ψ"] ="\xCE\xA8"; // Ψ (Psi) | ||
145 | $htmlEntities["Ω"] ="\xCE\xA9"; // Ω (Omega) | ||
146 | $htmlEntities["α"] ="\xCE\xB1"; // α (alpha) | ||
147 | $htmlEntities["β"] ="\xCE\xB2"; // β (beta) | ||
148 | $htmlEntities["γ"] ="\xCE\xB3"; // γ (gamma) | ||
149 | $htmlEntities["δ"] ="\xCE\xB4"; // δ (delta) | ||
150 | $htmlEntities["ε"] ="\xCE\xB5"; // ε (epsilon) | ||
151 | $htmlEntities["ζ"] ="\xCE\xB6"; // ζ (zeta) | ||
152 | $htmlEntities["η"] ="\xCE\xB7"; // η (eta) | ||
153 | $htmlEntities["θ"] ="\xCE\xB8"; // θ (theta) | ||
154 | $htmlEntities["ι"] ="\xCE\xB9"; // ι (iota) | ||
155 | $htmlEntities["κ"] ="\xCE\xBA"; // κ (kappa) | ||
156 | $htmlEntities["λ"] ="\xCE\xBB"; // λ (lambda) | ||
157 | $htmlEntities["μ"] ="\xCE\xBC"; // μ (mu) | ||
158 | $htmlEntities["ν"] ="\xCE\xBD"; // ν (nu) | ||
159 | $htmlEntities["ξ"] ="\xCE\xBE"; // ξ (xi) | ||
160 | $htmlEntities["ο"] ="\xCE\xBF"; // ο (omicron) | ||
161 | $htmlEntities["π"] ="\xCF\x80"; // π (pi) | ||
162 | $htmlEntities["ρ"] ="\xCF\x81"; // ρ (rho) | ||
163 | $htmlEntities["ς"] ="\xCF\x82"; // ς (sigmaf) | ||
164 | $htmlEntities["σ"] ="\xCF\x83"; // σ (sigma) | ||
165 | $htmlEntities["τ"] ="\xCF\x84"; // τ (tau) | ||
166 | $htmlEntities["υ"] ="\xCF\x85"; // υ (upsilon) | ||
167 | $htmlEntities["φ"] ="\xCF\x86"; // φ (phi) | ||
168 | $htmlEntities["χ"] ="\xCF\x87"; // χ (chi) | ||
169 | $htmlEntities["ψ"] ="\xCF\x88"; // ψ (psi) | ||
170 | $htmlEntities["ω"] ="\xCF\x89"; // ω (omega) | ||
171 | $htmlEntities["ϑ"] ="\xCF\x91"; // ϑ (theta symbol) | ||
172 | $htmlEntities["ϒ"] ="\xCF\x92"; // ϒ (upsilon symbol) | ||
173 | $htmlEntities["ϖ"] ="\xCF\x96"; // ϖ (pi symbol) | ||
174 | $htmlEntities[" "] ="\xE2\x80\x82"; //   (en space) | ||
175 | $htmlEntities[" "] ="\xE2\x80\x83"; //   (em space) | ||
176 | $htmlEntities[" "] ="\xE2\x80\x89"; //   (thin space) | ||
177 | $htmlEntities["‌"] ="\xE2\x80\x8C"; // ‌ (zero width non-joiner) | ||
178 | $htmlEntities["‍"] ="\xE2\x80\x8D"; // ‍ (zero width joiner) | ||
179 | $htmlEntities["‎"] ="\xE2\x80\x8E"; // ‎ (left-to-right mark) | ||
180 | $htmlEntities["‏"] ="\xE2\x80\x8F"; // ‏ (right-to-left mark) | ||
181 | $htmlEntities["–"] ="\xE2\x80\x93"; // – (en dash) | ||
182 | $htmlEntities["—"] ="\xE2\x80\x94"; // — (em dash) | ||
183 | $htmlEntities["‘"] ="\xE2\x80\x98"; // ‘ (left single quotation mark) | ||
184 | $htmlEntities["’"] ="\xE2\x80\x99"; // ’ (right single quotation mark) | ||
185 | $htmlEntities["‚"] ="\xE2\x80\x9A"; // ‚ (single low-9 quotation mark) | ||
186 | $htmlEntities["“"] ="\xE2\x80\x9C"; // “ (left double quotation mark) | ||
187 | $htmlEntities["”"] ="\xE2\x80\x9D"; // ” (right double quotation mark) | ||
188 | $htmlEntities["„"] ="\xE2\x80\x9E"; // „ (double low-9 quotation mark) | ||
189 | $htmlEntities["†"] ="\xE2\x80\xA0"; // † (dagger) | ||
190 | $htmlEntities["‡"] ="\xE2\x80\xA1"; // ‡ (double dagger) | ||
191 | $htmlEntities["•"] ="\xE2\x80\xA2"; // • (bullet) | ||
192 | $htmlEntities["…"] ="\xE2\x80\xA6"; // … (horizontal ellipsis) | ||
193 | $htmlEntities["‰"] ="\xE2\x80\xB0"; // ‰ (per mille) | ||
194 | $htmlEntities["′"] ="\xE2\x80\xB2"; // ′ (minutes or prime) | ||
195 | $htmlEntities["″"] ="\xE2\x80\xB3"; // ″ (seconds or Double Prime) | ||
196 | $htmlEntities["‹"] ="\xE2\x80\xB9"; // ‹ (single left angle quotation) | ||
197 | $htmlEntities["›"] ="\xE2\x80\xBA"; // › (single right angle quotation) | ||
198 | $htmlEntities["‾"] ="\xE2\x80\xBE"; // ‾ (overline) | ||
199 | $htmlEntities["⁄"] ="\xE2\x81\x84"; // ⁄ (fraction slash) | ||
200 | $htmlEntities["€"] ="\xE2\x82\xAC"; // € (euro) | ||
201 | $htmlEntities["ℑ"] ="\xE2\x84\x91"; // ℑ (blackletter capital I) | ||
202 | $htmlEntities["℘"] ="\xE2\x84\x98"; // ℘ (script capital P) | ||
203 | $htmlEntities["ℜ"] ="\xE2\x84\x9C"; // ℜ (blackletter capital R) | ||
204 | $htmlEntities["™"] ="\xE2\x84\xA2"; // ™ (trademark) | ||
205 | $htmlEntities["ℵ"] ="\xE2\x84\xB5"; // ℵ (alef) | ||
206 | $htmlEntities["←"] ="\xE2\x86\x90"; // ← (left arrow) | ||
207 | $htmlEntities["↑"] ="\xE2\x86\x91"; // ↑ (up arrow) | ||
208 | $htmlEntities["→"] ="\xE2\x86\x92"; // → (right arrow) | ||
209 | $htmlEntities["↓"] ="\xE2\x86\x93"; // ↓ (down arrow) | ||
210 | $htmlEntities["↔"] ="\xE2\x86\x94"; // ↔ (left right arrow) | ||
211 | $htmlEntities["↵"] ="\xE2\x86\xB5"; // ↵ (carriage return arrow) | ||
212 | $htmlEntities["⇐"] ="\xE2\x87\x90"; // ⇐ (left double arrow) | ||
213 | $htmlEntities["⇑"] ="\xE2\x87\x91"; // ⇑ (up double arrow) | ||
214 | $htmlEntities["⇒"] ="\xE2\x87\x92"; // ⇒ (right double arrow) | ||
215 | $htmlEntities["⇓"] ="\xE2\x87\x93"; // ⇓ (down double arrow) | ||
216 | $htmlEntities["⇔"] ="\xE2\x87\x94"; // ⇔ (left right double arrow) | ||
217 | $htmlEntities["∀"] ="\xE2\x88\x80"; // ∀ (for all) | ||
218 | $htmlEntities["∂"] ="\xE2\x88\x82"; // ∂ (partial differential) | ||
219 | $htmlEntities["∃"] ="\xE2\x88\x83"; // ∃ (there exists) | ||
220 | $htmlEntities["∅"] ="\xE2\x88\x85"; // ∅ (empty set) | ||
221 | $htmlEntities["∇"] ="\xE2\x88\x87"; // ∇ (backward difference) | ||
222 | $htmlEntities["∈"] ="\xE2\x88\x88"; // ∈ (element of) | ||
223 | $htmlEntities["∉"] ="\xE2\x88\x89"; // ∉ (not an element of) | ||
224 | $htmlEntities["∋"] ="\xE2\x88\x8B"; // ∋ (ni = contains as member) | ||
225 | $htmlEntities["∏"] ="\xE2\x88\x8F"; // ∏ (n-ary product) | ||
226 | $htmlEntities["∑"] ="\xE2\x88\x91"; // ∑ (n-ary sumation) | ||
227 | $htmlEntities["−"] ="\xE2\x88\x92"; // − (minus) | ||
228 | $htmlEntities["∗"] ="\xE2\x88\x97"; // ∗ (asterisk operator) | ||
229 | $htmlEntities["√"] ="\xE2\x88\x9A"; // √ (square root) | ||
230 | $htmlEntities["∝"] ="\xE2\x88\x9D"; // ∝ (proportional to) | ||
231 | $htmlEntities["∞"] ="\xE2\x88\x9E"; // ∞ (infinity) | ||
232 | $htmlEntities["∠"] ="\xE2\x88\xA0"; // ∠ (angle) | ||
233 | $htmlEntities["∧"] ="\xE2\x88\xA7"; // ∧ (logical and) | ||
234 | $htmlEntities["∨"] ="\xE2\x88\xA8"; // ∨ (logical or) | ||
235 | $htmlEntities["∩"] ="\xE2\x88\xA9"; // ∩ (intersection) | ||
236 | $htmlEntities["∪"] ="\xE2\x88\xAA"; // ∪ (union) | ||
237 | $htmlEntities["∫"] ="\xE2\x88\xAB"; // ∫ (integral) | ||
238 | $htmlEntities["∴"] ="\xE2\x88\xB4"; // ∴ (therefore) | ||
239 | $htmlEntities["∼"] ="\xE2\x88\xBC"; // ∼ (similar to) | ||
240 | $htmlEntities["≅"] ="\xE2\x89\x85"; // ≅ (congruent to) | ||
241 | $htmlEntities["≈"] ="\xE2\x89\x88"; // ≈ (approximately equal) | ||
242 | $htmlEntities["≠"] ="\xE2\x89\xA0"; // ≠ (not equal) | ||
243 | $htmlEntities["≡"] ="\xE2\x89\xA1"; // ≡ (equivalent) | ||
244 | $htmlEntities["≤"] ="\xE2\x89\xA4"; // ≤ (less or equal) | ||
245 | $htmlEntities["≥"] ="\xE2\x89\xA5"; // ≥ (greater or equal) | ||
246 | $htmlEntities["⊂"] ="\xE2\x8A\x82"; // ⊂ (subset of) | ||
247 | $htmlEntities["⊃"] ="\xE2\x8A\x83"; // ⊃ (superset of) | ||
248 | $htmlEntities["⊄"] ="\xE2\x8A\x84"; // ⊄ (not subset of) | ||
249 | $htmlEntities["⊆"] ="\xE2\x8A\x86"; // ⊆ (subset or equal) | ||
250 | $htmlEntities["⊇"] ="\xE2\x8A\x87"; // ⊇ (superset or equal) | ||
251 | $htmlEntities["⊕"] ="\xE2\x8A\x95"; // ⊕ (circled plus) | ||
252 | $htmlEntities["⊗"] ="\xE2\x8A\x87"; // ⊗ (circled times) | ||
253 | $htmlEntities["⊥"] ="\xE2\x8A\xA5"; // ⊥ (perpendicular) | ||
254 | $htmlEntities["⋅"] ="\xE2\x8C\x85"; // ⋅ (dot operator) | ||
255 | $htmlEntities["⌈"] ="\xE2\x8C\x88"; // ⌈ (left ceiling) | ||
256 | $htmlEntities["⌉"] ="\xE2\x8C\x89"; // ⌉ (right ceiling) | ||
257 | $htmlEntities["⌊"] ="\xE2\x8C\x8A"; // ⌊ (left floor) | ||
258 | $htmlEntities["⌋"] ="\xE2\x8C\x8B"; // ⌋ (right floor) | ||
259 | $htmlEntities["⟨"] ="\xE2\x8C\xA9"; // 〈 (left angle bracket = bra) | ||
260 | $htmlEntities["⟩"] ="\xE2\x8C\xAA"; // 〉 (right angle bracket = ket) | ||
261 | $htmlEntities["◊"] ="\xE2\x97\x8A"; // ◊ (lozenge) | ||
262 | $htmlEntities["♠"] ="\xE2\x99\xA0"; // ♠ (spade) | ||
263 | $htmlEntities["♣"] ="\xE2\x99\xA3"; // ♣ (club) | ||
264 | $htmlEntities["♥"] ="\xE2\x99\xA5"; // ♥ (heart) | ||
265 | $htmlEntities["♦"] ="\xE2\x99\xA6"; // ♦ (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 | */ | ||
10 | class 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 | */ | ||
382 | class 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 | */ | ||
508 | class 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 | */ | ||
10 | class 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 | */ | ||
232 | class 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 | */ | ||
318 | class 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 | */ | ||
439 | class 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 | */ | ||
490 | class 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 | */ | ||
633 | class 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 | */ | ||
700 | class 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 | */ | ||
773 | class 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 | */ | ||
838 | class 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 | */ | ||
981 | class MarcCode { | ||
982 | const _VERSION = 3.00; | ||
983 | |||
984 | /** | ||
985 | * Adapter | ||
986 | * | ||
987 | * Use for a person who | ||
988 | * 1) reworks a musical composition, usually for a different medium, or | ||
989 | * 2) rewrites novels or stories for motion pictures or other audiovisual medium. | ||
990 | */ | ||
991 | const ADAPTER = "adp"; | ||
992 | |||
993 | /** | ||
994 | * Annotator | ||
995 | * | ||
996 | * Use for a person who writes manuscript annotations on a printed item. | ||
997 | */ | ||
998 | const ANNOTATOR = "ann"; | ||
999 | |||
1000 | /** | ||
1001 | * Arranger | ||
1002 | * | ||
1003 | * Use for a person who transcribes a musical composition, usually for a different | ||
1004 | * medium from that of the original; in an arrangement the musical substance remains | ||
1005 | * essentially unchanged. | ||
1006 | */ | ||
1007 | const ARRANGER = "arr"; | ||
1008 | |||
1009 | /** | ||
1010 | * Artist | ||
1011 | * | ||
1012 | * Use for a person (e.g., a painter) who conceives, and perhaps also implements, | ||
1013 | * an original graphic design or work of art, if specific codes (e.g., [egr], | ||
1014 | * [etr]) are not desired. For book illustrators, prefer Illustrator [ill]. | ||
1015 | */ | ||
1016 | const ARTIST = "art"; | ||
1017 | |||
1018 | /** | ||
1019 | * Associated name | ||
1020 | * | ||
1021 | * Use as a general relator for a name associated with or found in an item or | ||
1022 | * collection, or which cannot be determined to be that of a Former owner [fmo] | ||
1023 | * or other designated relator indicative of provenance. | ||
1024 | */ | ||
1025 | const ASSOCIATED_NAME = "asn"; | ||
1026 | |||
1027 | /** | ||
1028 | * Author | ||
1029 | * | ||
1030 | * Use for a person or corporate body chiefly responsible for the intellectual | ||
1031 | * or artistic content of a work. This term may also be used when more than one | ||
1032 | * person or body bears such responsibility. | ||
1033 | */ | ||
1034 | const AUTHOR = "aut"; | ||
1035 | |||
1036 | /** | ||
1037 | * Author in quotations or text extracts | ||
1038 | * | ||
1039 | * Use for a person whose work is largely quoted or extracted in a works to which | ||
1040 | * he or she did not contribute directly. Such quotations are found particularly | ||
1041 | * in exhibition catalogs, collections of photographs, etc. | ||
1042 | */ | ||
1043 | const AUTHOR_IN_QUOTES = "aqt"; | ||
1044 | |||
1045 | /** | ||
1046 | * Author of afterword, colophon, etc. | ||
1047 | * | ||
1048 | * Use for a person or corporate body responsible for an afterword, postface, | ||
1049 | * colophon, etc. but who is not the chief author of a work. | ||
1050 | */ | ||
1051 | const AUTHOR_OF_AFTERWORD = "aft"; | ||
1052 | |||
1053 | /** | ||
1054 | * Author of introduction, etc. | ||
1055 | * | ||
1056 | * Use for a person or corporate body responsible for an introduction, preface, | ||
1057 | * foreword, or other critical matter, but who is not the chief author. | ||
1058 | */ | ||
1059 | const AUTHOR_OF_INTRO = "aui"; | ||
1060 | |||
1061 | /** | ||
1062 | * Bibliographic antecedent | ||
1063 | * | ||
1064 | * Use for the author responsible for a work upon which the work represented by | ||
1065 | * the catalog record is based. This can be appropriate for adaptations, sequels, | ||
1066 | * continuations, indexes, etc. | ||
1067 | */ | ||
1068 | const BIB_ANTECEDENT = "ant"; | ||
1069 | |||
1070 | /** | ||
1071 | * Book producer | ||
1072 | * | ||
1073 | * Use for the person or firm responsible for the production of books and other | ||
1074 | * print media, if specific codes (e.g., [bkd], [egr], [tyd], [prt]) are not desired. | ||
1075 | */ | ||
1076 | const BOOK_PRODUCER = "bkp"; | ||
1077 | |||
1078 | /** | ||
1079 | * Collaborator | ||
1080 | * | ||
1081 | * Use for a person or corporate body that takes a limited part in the elaboration | ||
1082 | * of a work of another author or that brings complements (e.g., appendices, notes) | ||
1083 | * to the work of another author. | ||
1084 | */ | ||
1085 | const COLABORATOR = "clb"; | ||
1086 | |||
1087 | /** | ||
1088 | * Commentator | ||
1089 | * | ||
1090 | * Use for a person who provides interpretation, analysis, or a discussion of the | ||
1091 | * subject matter on a recording, motion picture, or other audiovisual medium. | ||
1092 | * Compiler [com] Use for a person who produces a work or publication by selecting | ||
1093 | * and putting together material from the works of various persons or bodies. | ||
1094 | */ | ||
1095 | const COMMENTATOR = "cmm"; | ||
1096 | |||
1097 | /** | ||
1098 | * Designer | ||
1099 | * | ||
1100 | * Use for a person or organization responsible for design if specific codes (e.g., | ||
1101 | * [bkd], [tyd]) are not desired. | ||
1102 | */ | ||
1103 | const DESIGNER = "dsr"; | ||
1104 | |||
1105 | /** | ||
1106 | * Editor | ||
1107 | * | ||
1108 | * Use for a person who prepares for publication a work not primarily his/her own, | ||
1109 | * such as by elucidating text, adding introductory or other critical matter, or | ||
1110 | * technically directing an editorial staff. | ||
1111 | */ | ||
1112 | const EDITORT = "edt"; | ||
1113 | |||
1114 | /** | ||
1115 | * Illustrator | ||
1116 | * | ||
1117 | * Use for the person who conceives, and perhaps also implements, a design or | ||
1118 | * illustration, usually to accompany a written text. | ||
1119 | */ | ||
1120 | const ILLUSTRATOR = "ill"; | ||
1121 | |||
1122 | /** | ||
1123 | * Lyricist | ||
1124 | * | ||
1125 | * Use for the writer of the text of a song. | ||
1126 | */ | ||
1127 | const LYRICIST = "lyr"; | ||
1128 | |||
1129 | /** | ||
1130 | * Metadata contact | ||
1131 | * | ||
1132 | * Use for the person or organization primarily responsible for compiling and | ||
1133 | * maintaining the original description of a metadata set (e.g., geospatial | ||
1134 | * metadata set). | ||
1135 | */ | ||
1136 | const METADATA_CONTACT = "mdc"; | ||
1137 | |||
1138 | /** | ||
1139 | * Musician | ||
1140 | * | ||
1141 | * Use for the person who performs music or contributes to the musical content | ||
1142 | * of a work when it is not possible or desirable to identify the function more | ||
1143 | * precisely. | ||
1144 | */ | ||
1145 | const MUSICIAN = "mus"; | ||
1146 | |||
1147 | /** | ||
1148 | * Narrator | ||
1149 | * | ||
1150 | * Use for the speaker who relates the particulars of an act, occurrence, or | ||
1151 | * course of events. | ||
1152 | */ | ||
1153 | const NARRATOR = "nrt"; | ||
1154 | |||
1155 | /** | ||
1156 | * Other | ||
1157 | * | ||
1158 | * Use for relator codes from other lists which have no equivalent in the MARC | ||
1159 | * list or for terms which have not been assigned a code. | ||
1160 | */ | ||
1161 | const OTHER = "oth"; | ||
1162 | |||
1163 | /** | ||
1164 | * Photographer | ||
1165 | * | ||
1166 | * Use for the person or organization responsible for taking photographs, whether | ||
1167 | * they are used in their original form or as reproductions. | ||
1168 | */ | ||
1169 | const PHOTOGRAPHER = "pht"; | ||
1170 | |||
1171 | /** | ||
1172 | * Printer | ||
1173 | * | ||
1174 | * Use for the person or organization who prints texts, whether from type or plates. | ||
1175 | */ | ||
1176 | const PRINTER = "prt"; | ||
1177 | |||
1178 | /** | ||
1179 | * Redactor | ||
1180 | * | ||
1181 | * Use for a person who writes or develops the framework for an item without | ||
1182 | * being intellectually responsible for its content. | ||
1183 | */ | ||
1184 | const REDACTOR = "red"; | ||
1185 | |||
1186 | /** | ||
1187 | * Reviewer | ||
1188 | * | ||
1189 | * Use for a person or corporate body responsible for the review of book, motion | ||
1190 | * picture, performance, etc. | ||
1191 | */ | ||
1192 | const REVIEWER = "rev"; | ||
1193 | |||
1194 | /** | ||
1195 | * Sponsor | ||
1196 | * | ||
1197 | * Use for the person or agency that issued a contract, or under whose auspices | ||
1198 | * a work has been written, printed, published, etc. | ||
1199 | */ | ||
1200 | const SPONSOR = "spn"; | ||
1201 | |||
1202 | /** | ||
1203 | * Thesis advisor | ||
1204 | * | ||
1205 | * Use for the person under whose supervision a degree candidate develops and | ||
1206 | * presents a thesis, memoir, or text of a dissertation. | ||
1207 | */ | ||
1208 | const THESIS_ADVISOR = "ths"; | ||
1209 | |||
1210 | /** | ||
1211 | * Transcriber | ||
1212 | * | ||
1213 | * Use for a person who prepares a handwritten or typewritten copy from original | ||
1214 | * material, including from dictated or orally recorded material. | ||
1215 | */ | ||
1216 | const TRANSCRIBER = "trc"; | ||
1217 | |||
1218 | /** | ||
1219 | * Translator | ||
1220 | * | ||
1221 | * Use for a person who renders a text from one language into another, or from | ||
1222 | * an older form of a language into the modern form. | ||
1223 | */ | ||
1224 | const TRANSLATOR = "trl"; | ||
1225 | } | ||
1226 | ?> | ||
diff --git a/inc/3rdparty/libraries/PHPePub/EPub.php b/inc/3rdparty/libraries/PHPePub/EPub.php new file mode 100644 index 00000000..836c0512 --- /dev/null +++ b/inc/3rdparty/libraries/PHPePub/EPub.php | |||
@@ -0,0 +1,2429 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * Create an ePub compatible book file. | ||
4 | * | ||
5 | * Please note, once finalized a book can no longer have chapters of data added or changed. | ||
6 | * | ||
7 | * License: GNU LGPL, Attribution required for commercial implementations, requested for everything else. | ||
8 | * | ||
9 | * Thanks to: Adam Schmalhofer and Kirstyn Fox for invaluable input and for "nudging" me in the right direction :) | ||
10 | * | ||
11 | * @author A. Grandt <php@grandt.com> | ||
12 | * @copyright 2009-2014 A. Grandt | ||
13 | * @license GNU LGPL 2.1 | ||
14 | * @version 3.20 | ||
15 | * @link http://www.phpclasses.org/package/6115 | ||
16 | * @link https://github.com/Grandt/PHPePub | ||
17 | * @uses Zip.php version 1.50; http://www.phpclasses.org/browse/package/6110.html or https://github.com/Grandt/PHPZip | ||
18 | */ | ||
19 | class EPub { | ||
20 | const VERSION = 3.20; | ||
21 | const REQ_ZIP_VERSION = 1.60; | ||
22 | |||
23 | const IDENTIFIER_UUID = 'UUID'; | ||
24 | const IDENTIFIER_URI = 'URI'; | ||
25 | const IDENTIFIER_ISBN = 'ISBN'; | ||
26 | |||
27 | /** Ignore all external references, and do not process the file for these */ | ||
28 | const EXTERNAL_REF_IGNORE = 0; | ||
29 | /** Process the file for external references and add them to the book */ | ||
30 | const EXTERNAL_REF_ADD = 1; | ||
31 | /** Process the file for external references and add them to the book, but remove images, and img tags */ | ||
32 | const EXTERNAL_REF_REMOVE_IMAGES = 2; | ||
33 | /** Process the file for external references and add them to the book, but replace images, and img tags with [image] */ | ||
34 | const EXTERNAL_REF_REPLACE_IMAGES = 3; | ||
35 | |||
36 | const DIRECTION_LEFT_TO_RIGHT = "ltr"; | ||
37 | const DIRECTION_RIGHT_TO_LEFT = "rtl"; | ||
38 | |||
39 | const BOOK_VERSION_EPUB2 = "2.0"; | ||
40 | const BOOK_VERSION_EPUB3 = "3.0"; | ||
41 | |||
42 | private $bookVersion = EPub::BOOK_VERSION_EPUB2; | ||
43 | |||
44 | public $maxImageWidth = 768; | ||
45 | public $maxImageHeight = 1024; | ||
46 | |||
47 | public $splitDefaultSize = 250000; | ||
48 | /** Gifs can crash some early ADE based readers, and are disabled by default. | ||
49 | * getImage will convert these if it can, unless this is set to TRUE. | ||
50 | */ | ||
51 | public $isGifImagesEnabled = FALSE; | ||
52 | public $isReferencesAddedToToc = TRUE; | ||
53 | |||
54 | private $zip; | ||
55 | |||
56 | private $title = ""; | ||
57 | private $language = "en"; | ||
58 | private $identifier = ""; | ||
59 | private $identifierType = ""; | ||
60 | private $description = ""; | ||
61 | private $author = ""; | ||
62 | private $authorSortKey = ""; | ||
63 | private $publisherName = ""; | ||
64 | private $publisherURL = ""; | ||
65 | private $date = 0; | ||
66 | private $rights = ""; | ||
67 | private $coverage = ""; | ||
68 | private $relation = ""; | ||
69 | private $sourceURL = ""; | ||
70 | |||
71 | private $chapterCount = 0; | ||
72 | private $opf = NULL; | ||
73 | private $ncx = NULL; | ||
74 | private $isFinalized = FALSE; | ||
75 | private $isCoverImageSet = FALSE; | ||
76 | private $buildTOC = FALSE; | ||
77 | private $tocTitle = NULL; | ||
78 | private $tocFileName = NULL; | ||
79 | private $tocCSSClass = NULL; | ||
80 | private $tocAddReferences = FALSE; | ||
81 | private $tocCssFileName = NULL; | ||
82 | |||
83 | private $fileList = array(); | ||
84 | private $writingDirection = EPub::DIRECTION_LEFT_TO_RIGHT; | ||
85 | private $languageCode = "en"; | ||
86 | |||
87 | /** | ||
88 | * Used for building the TOC. | ||
89 | * If this list is overwritten it MUST contain at least "text" as an element. | ||
90 | */ | ||
91 | public $referencesOrder = NULL; | ||
92 | |||
93 | private $dateformat = 'Y-m-d\TH:i:s.000000P'; // ISO 8601 long | ||
94 | private $dateformatShort = 'Y-m-d'; // short date format to placate ePubChecker. | ||
95 | private $headerDateFormat = "D, d M Y H:i:s T"; | ||
96 | |||
97 | protected $isCurlInstalled; | ||
98 | protected $isGdInstalled; | ||
99 | protected $isExifInstalled; | ||
100 | protected $isFileGetContentsInstalled; | ||
101 | protected $isFileGetContentsExtInstalled; | ||
102 | |||
103 | private $bookRoot = "OEBPS/"; | ||
104 | private $docRoot = NULL; | ||
105 | private $EPubMark = TRUE; | ||
106 | private $generator = ""; | ||
107 | |||
108 | private $log = NULL; | ||
109 | public $isLogging = TRUE; | ||
110 | |||
111 | public $encodeHTML = FALSE; | ||
112 | |||
113 | private $mimetypes = array( | ||
114 | "js" => "application/x-javascript", "swf" => "application/x-shockwave-flash", "xht" => "application/xhtml+xml", "xhtml" => "application/xhtml+xml", "zip" => "application/zip", | ||
115 | "aif" => "audio/x-aiff", "aifc" => "audio/x-aiff", "aiff" => "audio/x-aiff", "au" => "audio/basic", "kar" => "audio/midi", "m3u" => "audio/x-mpegurl", "mid" => "audio/midi", "midi" => "audio/midi", "mp2" => "audio/mpeg", "mp3" => "audio/mpeg", "mpga" => "audio/mpeg", "oga" => "audio/ogg", "ogg" => "audio/ogg", "ra" => "audio/x-realaudio", "ram" => "audio/x-pn-realaudio", "rm" => "audio/x-pn-realaudio", "rpm" => "audio/x-pn-realaudio-plugin", "snd" => "audio/basic", "wav" => "audio/x-wav", | ||
116 | "bmp" => "image/bmp", "djv" => "image/vnd.djvu", "djvu" => "image/vnd.djvu", "gif" => "image/gif", "ief" => "image/ief", "jpe" => "image/jpeg", "jpeg" => "image/jpeg", "jpg" => "image/jpeg", "pbm" => "image/x-portable-bitmap", "pgm" => "image/x-portable-graymap", "png" => "image/png", "pnm" => "image/x-portable-anymap", "ppm" => "image/x-portable-pixmap", "ras" => "image/x-cmu-raster", "rgb" => "image/x-rgb", "tif" => "image/tif", "tiff" => "image/tiff", "wbmp" => "image/vnd.wap.wbmp", "xbm" => "image/x-xbitmap", "xpm" => "image/x-xpixmap", "xwd" => "image/x-windowdump", | ||
117 | "asc" => "text/plain", "css" => "text/css", "etx" => "text/x-setext", "htm" => "text/html", "html" => "text/html", "rtf" => "text/rtf", "rtx" => "text/richtext", "sgm" => "text/sgml", "sgml" => "text/sgml", "tsv" => "text/tab-seperated-values", "txt" => "text/plain", "wml" => "text/vnd.wap.wml", "wmls" => "text/vnd.wap.wmlscript", "xml" => "text/xml", "xsl" => "text/xml", | ||
118 | "avi" => "video/x-msvideo", "mov" => "video/quicktime", "movie" => "video/x-sgi-movie", "mp4" => "video/mp4", "mpe" => "video/mpeg", "mpeg" => "video/mpeg", "mpg" => "video/mpeg", "mxu" => "video/vnd.mpegurl", "ogv" => "video/ogg", "qt" => "video/quicktime", "webm" => "video/webm"); | ||
119 | |||
120 | // These are the ONLY allowed types in that these are the ones ANY reader must support, any other MUST have the fallback attribute pointing to one of these. | ||
121 | private $coreMediaTypes = array("image/gif", "image/jpeg", "image/png", "image/svg+xml", "application/xhtml+xml", "application/x-dtbook+xml", "application/xml", "application/x-dtbncx+xml", "text/css", "text/x-oeb1-css", "text/x-oeb1-document"); | ||
122 | |||
123 | private $opsContentTypes = array("application/xhtml+xml", "application/x-dtbook+xml", "application/xml", "application/x-dtbncx+xml", "text/x-oeb1-document"); | ||
124 | |||
125 | private $forbiddenCharacters = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%"); | ||
126 | |||
127 | private $htmlContentHeader = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n<title></title>\n</head>\n<body>\n"; | ||
128 | private $htmlContentFooter = "</body>\n</html>\n"; | ||
129 | |||
130 | /** | ||
131 | * Class constructor. | ||
132 | * | ||
133 | * @return void | ||
134 | */ | ||
135 | function __construct($bookVersion = EPub::BOOK_VERSION_EPUB2, $languageCode = "en", $writingDirection = EPub::DIRECTION_LEFT_TO_RIGHT) { | ||
136 | include_once("Zip.php"); | ||
137 | include_once("Logger.php"); | ||
138 | |||
139 | $this->bookVersion = $bookVersion; | ||
140 | $this->writingDirection = $writingDirection; | ||
141 | $this->languageCode = $languageCode; | ||
142 | |||
143 | $this->log = new Logger("EPub", $this->isLogging); | ||
144 | |||
145 | /* Prepare Logging. Just in case it's used. later */ | ||
146 | if ($this->isLogging) { | ||
147 | $this->log->logLine("EPub class version....: " . self::VERSION); | ||
148 | $this->log->logLine("EPub req. Zip version.: " . self::REQ_ZIP_VERSION); | ||
149 | $this->log->logLine("Zip version...........: " . Zip::VERSION); | ||
150 | $this->log->dumpInstalledModules(); | ||
151 | } | ||
152 | |||
153 | if (!defined("Zip::VERSION") || Zip::VERSION < self::REQ_ZIP_VERSION) { | ||
154 | die("<p>EPub version " . self::VERSION . " requires Zip.php at version " . self::REQ_ZIP_VERSION . " or higher.<br />You can obtain the latest version from <a href=\"http://www.phpclasses.org/browse/package/6110.html\">http://www.phpclasses.org/browse/package/6110.html</a>.</p>"); | ||
155 | } | ||
156 | |||
157 | include_once("EPubChapterSplitter.php"); | ||
158 | include_once("EPub.HtmlEntities.php"); | ||
159 | include_once("EPub.NCX.php"); | ||
160 | include_once("EPub.OPF.php"); | ||
161 | |||
162 | $this->initialize(); | ||
163 | } | ||
164 | |||
165 | /** | ||
166 | * Class destructor | ||
167 | * | ||
168 | * @return void | ||
169 | * @TODO make sure elements in the destructor match the current class elements | ||
170 | */ | ||
171 | function __destruct() { | ||
172 | unset($this->bookVersion, $this->maxImageWidth, $this->maxImageHeight); | ||
173 | unset($this->splitDefaultSize, $this->isGifImagesEnabled, $this->isReferencesAddedToToc); | ||
174 | unset($this->zip, $this->title, $this->language, $this->identifier, $this->identifierType); | ||
175 | unset($this->description, $this->author, $this->authorSortKey, $this->publisherName); | ||
176 | unset($this->publisherURL, $this->date, $this->rights, $this->coverage, $this->relation); | ||
177 | unset($this->sourceURL, $this->chapterCount, $this->opf, $this->ncx, $this->isFinalized); | ||
178 | unset($this->isCoverImageSet, $this->fileList, $this->writingDirection, $this->languageCode); | ||
179 | unset($this->referencesOrder, $this->dateformat, $this->dateformatShort, $this->headerDateFormat); | ||
180 | unset($this->isCurlInstalled, $this->isGdInstalled, $this->isExifInstalled); | ||
181 | unset($this->isFileGetContentsInstalled, $this->isFileGetContentsExtInstalled, $this->bookRoot); | ||
182 | unset($this->docRoot, $this->EPubMark, $this->generator, $this->log, $this->isLogging); | ||
183 | unset($this->encodeHTML, $this->mimetypes, $this->coreMediaTypes, $this->opsContentTypes); | ||
184 | unset($this->forbiddenCharacters, $this->htmlContentHeader, $this->htmlContentFooter); | ||
185 | unset($this->buildTOC, $this->tocTitle, $this->tocCSSClass, $this->tocAddReferences); | ||
186 | unset($this->tocFileName, $this->tocCssFileName); | ||
187 | } | ||
188 | |||
189 | /** | ||
190 | * initialize defaults. | ||
191 | */ | ||
192 | private function initialize() { | ||
193 | $this->referencesOrder = array( | ||
194 | Reference::COVER => "Cover Page", | ||
195 | Reference::TITLE_PAGE => "Title Page", | ||
196 | Reference::ACKNOWLEDGEMENTS => "Acknowledgements", | ||
197 | Reference::BIBLIOGRAPHY => "Bibliography", | ||
198 | Reference::COLOPHON => "Colophon", | ||
199 | Reference::COPYRIGHT_PAGE => "Copyright", | ||
200 | Reference::DEDICATION => "Dedication", | ||
201 | Reference::EPIGRAPH => "Epigraph", | ||
202 | Reference::FOREWORD => "Foreword", | ||
203 | Reference::TABLE_OF_CONTENTS => "Table of Contents", | ||
204 | Reference::NOTES => "Notes", | ||
205 | Reference::PREFACE => "Preface", | ||
206 | Reference::TEXT => "First Page", | ||
207 | Reference::LIST_OF_ILLUSTRATIONS => "List of Illustrations", | ||
208 | Reference::LIST_OF_TABLES => "List of Tables", | ||
209 | Reference::GLOSSARY => "Glossary", | ||
210 | Reference::INDEX => "Index"); | ||
211 | |||
212 | $this->docRoot = filter_input(INPUT_SERVER, "DOCUMENT_ROOT") . "/"; | ||
213 | |||
214 | $this->isCurlInstalled = extension_loaded('curl') && function_exists('curl_version'); | ||
215 | $this->isGdInstalled = extension_loaded('gd') && function_exists('gd_info'); | ||
216 | $this->isExifInstalled = extension_loaded('exif') && function_exists('exif_imagetype'); | ||
217 | $this->isFileGetContentsInstalled = function_exists('file_get_contents'); | ||
218 | $this->isFileGetContentsExtInstalled = $this->isFileGetContentsInstalled && ini_get('allow_url_fopen'); | ||
219 | |||
220 | $this->zip = new Zip(); | ||
221 | $this->zip->setExtraField(FALSE); | ||
222 | $this->zip->addFile("application/epub+zip", "mimetype"); | ||
223 | $this->zip->setExtraField(TRUE); | ||
224 | $this->zip->addDirectory("META-INF"); | ||
225 | |||
226 | $this->content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<container version=\"1.0\" xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\">\n\t<rootfiles>\n\t\t<rootfile full-path=\"" . $this->bookRoot . "book.opf\" media-type=\"application/oebps-package+xml\" />\n\t</rootfiles>\n</container>\n"; | ||
227 | |||
228 | if (!$this->isEPubVersion2()) { | ||
229 | $this->htmlContentHeader = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" | ||
230 | . "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n" | ||
231 | . "<head>" | ||
232 | . "<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n" | ||
233 | . "<title></title>\n" | ||
234 | . "</head>\n" | ||
235 | . "<body>\n"; | ||
236 | } | ||
237 | |||
238 | $this->zip->addFile($this->content, "META-INF/container.xml", 0, NULL, FALSE); | ||
239 | $this->content = NULL; | ||
240 | $this->ncx = new Ncx(NULL, NULL, NULL, $this->languageCode, $this->writingDirection); | ||
241 | $this->opf = new Opf(); | ||
242 | $this->ncx->setVersion($this->bookVersion); | ||
243 | $this->opf->setVersion($this->bookVersion); | ||
244 | $this->opf->addItem("ncx", "book.ncx", Ncx::MIMETYPE); | ||
245 | $this->chapterCount = 0; | ||
246 | } | ||
247 | |||
248 | /** | ||
249 | * Add dynamically generated data as a file to the book. | ||
250 | * | ||
251 | * @param string $fileName Filename to use for the file, must be unique for the book. | ||
252 | * @param string $fileId Unique identifier for the file. | ||
253 | * @param string $fileData File data | ||
254 | * @param string $mimetype file mime type | ||
255 | * @return bool $success | ||
256 | */ | ||
257 | function addFile($fileName, $fileId, $fileData, $mimetype) { | ||
258 | if ($this->isFinalized || array_key_exists($fileName, $this->fileList)) { | ||
259 | return FALSE; | ||
260 | } | ||
261 | |||
262 | $fileName = $this->normalizeFileName($fileName); | ||
263 | |||
264 | $compress = (strpos($mimetype, "image/") !== 0); | ||
265 | |||
266 | $this->zip->addFile($fileData, $this->bookRoot.$fileName, 0, NULL, $compress); | ||
267 | $this->fileList[$fileName] = $fileName; | ||
268 | $this->opf->addItem($fileId, $fileName, $mimetype); | ||
269 | return TRUE; | ||
270 | } | ||
271 | |||
272 | /** | ||
273 | * Add a large file directly from the filestystem to the book. | ||
274 | * | ||
275 | * @param string $fileName Filename to use for the file, must be unique for the book. | ||
276 | * @param string $fileId Unique identifier for the file. | ||
277 | * @param string $filePath File path | ||
278 | * @param string $mimetype file mime type | ||
279 | * @return bool $success | ||
280 | */ | ||
281 | function addLargeFile($fileName, $fileId, $filePath, $mimetype) { | ||
282 | if ($this->isFinalized || array_key_exists($fileName, $this->fileList)) { | ||
283 | return FALSE; | ||
284 | } | ||
285 | $fileName = $this->normalizeFileName($fileName); | ||
286 | |||
287 | if ($this->zip->addLargeFile($filePath, $this->bookRoot.$fileName)) { | ||
288 | $this->fileList[$fileName] = $fileName; | ||
289 | $this->opf->addItem($fileId, $fileName, $mimetype); | ||
290 | return TRUE; | ||
291 | } | ||
292 | return FALSE; | ||
293 | } | ||
294 | |||
295 | /** | ||
296 | * Add a CSS file to the book. | ||
297 | * | ||
298 | * @param string $fileName Filename to use for the CSS file, must be unique for the book. | ||
299 | * @param string $fileId Unique identifier for the file. | ||
300 | * @param string $fileData CSS data | ||
301 | * @param int $externalReferences How to handle external references, EPub::EXTERNAL_REF_IGNORE, EPub::EXTERNAL_REF_ADD or EPub::EXTERNAL_REF_REMOVE_IMAGES? See documentation for <code>processCSSExternalReferences</code> for explanation. Default is EPub::EXTERNAL_REF_IGNORE. | ||
302 | * @param string $baseDir Default is "", meaning it is pointing to the document root. NOT used if $externalReferences is set to EPub::EXTERNAL_REF_IGNORE. | ||
303 | * | ||
304 | * @return bool $success | ||
305 | */ | ||
306 | function addCSSFile($fileName, $fileId, $fileData, $externalReferences = EPub::EXTERNAL_REF_IGNORE, $baseDir = "") { | ||
307 | if ($this->isFinalized || array_key_exists($fileName, $this->fileList)) { | ||
308 | return FALSE; | ||
309 | } | ||
310 | $fileName = Zip::getRelativePath($fileName); | ||
311 | $fileName = preg_replace('#^[/\.]+#i', "", $fileName); | ||
312 | |||
313 | if ($externalReferences !== EPub::EXTERNAL_REF_IGNORE) { | ||
314 | $cssDir = pathinfo($fileName); | ||
315 | $cssDir = preg_replace('#^[/\.]+#i', "", $cssDir["dirname"] . "/"); | ||
316 | if (!empty($cssDir)) { | ||
317 | $cssDir = preg_replace('#[^/]+/#i', "../", $cssDir); | ||
318 | } | ||
319 | |||
320 | $this->processCSSExternalReferences($fileData, $externalReferences, $baseDir, $cssDir); | ||
321 | } | ||
322 | |||
323 | $this->addFile($fileName, "css_" . $fileId, $fileData, "text/css"); | ||
324 | |||
325 | return TRUE; | ||
326 | } | ||
327 | |||
328 | /** | ||
329 | * Add a chapter to the book, as a chapter should not exceed 250kB, you can parse an array with multiple parts as $chapterData. | ||
330 | * These will still only show up as a single chapter in the book TOC. | ||
331 | * | ||
332 | * @param string $chapterName Name of the chapter, will be use din the TOC | ||
333 | * @param string $fileName Filename to use for the chapter, must be unique for the book. | ||
334 | * @param string $chapter Chapter text in XHTML or array $chapterData valid XHTML data for the chapter. File should NOT exceed 250kB. | ||
335 | * @param bool $autoSplit Should the chapter be split if it exceeds the default split size? Default=FALSE, only used if $chapterData is a string. | ||
336 | * @param int $externalReferences How to handle external references, EPub::EXTERNAL_REF_IGNORE, EPub::EXTERNAL_REF_ADD or EPub::EXTERNAL_REF_REMOVE_IMAGES? See documentation for <code>processChapterExternalReferences</code> for explanation. Default is EPub::EXTERNAL_REF_IGNORE. | ||
337 | * @param string $baseDir Default is "", meaning it is pointing to the document root. NOT used if $externalReferences is set to EPub::EXTERNAL_REF_IGNORE. | ||
338 | * @return mixed $success FALSE if the addition failed, else the new NavPoint. | ||
339 | */ | ||
340 | function addChapter($chapterName, $fileName, $chapterData = NULL, $autoSplit = FALSE, $externalReferences = EPub::EXTERNAL_REF_IGNORE, $baseDir = "") { | ||
341 | if ($this->isFinalized) { | ||
342 | return FALSE; | ||
343 | } | ||
344 | $fileName = Zip::getRelativePath($fileName); | ||
345 | $fileName = preg_replace('#^[/\.]+#i', "", $fileName); | ||
346 | |||
347 | $chapter = $chapterData; | ||
348 | if ($autoSplit && is_string($chapterData) && mb_strlen($chapterData) > $this->splitDefaultSize) { | ||
349 | $splitter = new EPubChapterSplitter(); | ||
350 | |||
351 | $chapterArray = $splitter->splitChapter($chapterData); | ||
352 | if (count($chapterArray) > 1) { | ||
353 | $chapter = $chapterArray; | ||
354 | } | ||
355 | } | ||
356 | |||
357 | if (!empty($chapter) && is_string($chapter)) { | ||
358 | if ($externalReferences !== EPub::EXTERNAL_REF_IGNORE) { | ||
359 | $htmlDirInfo = pathinfo($fileName); | ||
360 | $htmlDir = preg_replace('#^[/\.]+#i', "", $htmlDirInfo["dirname"] . "/"); | ||
361 | $this->processChapterExternalReferences($chapter, $externalReferences, $baseDir, $htmlDir); | ||
362 | } | ||
363 | |||
364 | if ($this->encodeHTML === TRUE) { | ||
365 | $chapter = $this->encodeHtml($chapter); | ||
366 | } | ||
367 | |||
368 | $this->chapterCount++; | ||
369 | $this->addFile($fileName, "chapter" . $this->chapterCount, $chapter, "application/xhtml+xml"); | ||
370 | $this->opf->addItemRef("chapter" . $this->chapterCount); | ||
371 | |||
372 | $navPoint = new NavPoint($this->decodeHtmlEntities($chapterName), $fileName, "chapter" . $this->chapterCount); | ||
373 | $this->ncx->addNavPoint($navPoint); | ||
374 | $this->ncx->chapterList[$chapterName] = $navPoint; | ||
375 | } else if (is_array($chapter)) { | ||
376 | $fileNameParts = pathinfo($fileName); | ||
377 | $extension = $fileNameParts['extension']; | ||
378 | $name = $fileNameParts['filename']; | ||
379 | |||
380 | $partCount = 0; | ||
381 | $this->chapterCount++; | ||
382 | |||
383 | $oneChapter = each($chapter); | ||
384 | while ($oneChapter) { | ||
385 | list($k, $v) = $oneChapter; | ||
386 | if ($this->encodeHTML === TRUE) { | ||
387 | $v = $this->encodeHtml($v); | ||
388 | } | ||
389 | |||
390 | if ($externalReferences !== EPub::EXTERNAL_REF_IGNORE) { | ||
391 | $this->processChapterExternalReferences($v, $externalReferences, $baseDir); | ||
392 | } | ||
393 | $partCount++; | ||
394 | $partName = $name . "_" . $partCount; | ||
395 | $this->addFile($partName . "." . $extension, $partName, $v, "application/xhtml+xml"); | ||
396 | $this->opf->addItemRef($partName); | ||
397 | |||
398 | $oneChapter = each($chapter); | ||
399 | } | ||
400 | $partName = $name . "_1." . $extension; | ||
401 | $navPoint = new NavPoint($this->decodeHtmlEntities($chapterName), $partName, $partName); | ||
402 | $this->ncx->addNavPoint($navPoint); | ||
403 | |||
404 | $this->ncx->chapterList[$chapterName] = $navPoint; | ||
405 | } else if (!isset($chapterData) && strpos($fileName, "#") > 0) { | ||
406 | $this->chapterCount++; | ||
407 | //$this->opf->addItemRef("chapter" . $this->chapterCount); | ||
408 | |||
409 | $navPoint = new NavPoint($this->decodeHtmlEntities($chapterName), $fileName, "chapter" . $this->chapterCount); | ||
410 | $this->ncx->addNavPoint($navPoint); | ||
411 | $this->ncx->chapterList[$chapterName] = $navPoint; | ||
412 | } else if (!isset($chapterData) && $fileName=="TOC.xhtml") { | ||
413 | $this->chapterCount++; | ||
414 | $this->opf->addItemRef("toc"); | ||
415 | |||
416 | $navPoint = new NavPoint($this->decodeHtmlEntities($chapterName), $fileName, "chapter" . $this->chapterCount); | ||
417 | $this->ncx->addNavPoint($navPoint); | ||
418 | $this->ncx->chapterList[$chapterName] = $navPoint; | ||
419 | } | ||
420 | return $navPoint; | ||
421 | } | ||
422 | |||
423 | /** | ||
424 | * Add one chapter level. | ||
425 | * | ||
426 | * Subsequent chapters will be added to this level. | ||
427 | * | ||
428 | * @param string $navTitle | ||
429 | * @param string $navId | ||
430 | * @param string $navClass | ||
431 | * @param int $isNavHidden | ||
432 | * @param string $writingDirection | ||
433 | * @return NavPoint The new NavPoint for that level. | ||
434 | */ | ||
435 | function subLevel($navTitle = NULL, $navId = NULL, $navClass = NULL, $isNavHidden = FALSE, $writingDirection = NULL) { | ||
436 | return $this->ncx->subLevel($this->decodeHtmlEntities($navTitle), $navId, $navClass, $isNavHidden, $writingDirection); | ||
437 | } | ||
438 | |||
439 | /** | ||
440 | * Step back one chapter level. | ||
441 | * | ||
442 | * Subsequent chapters will be added to this chapters parent level. | ||
443 | */ | ||
444 | function backLevel() { | ||
445 | $this->ncx->backLevel(); | ||
446 | } | ||
447 | |||
448 | /** | ||
449 | * Step back to the root level. | ||
450 | * | ||
451 | * Subsequent chapters will be added to the rooot NavMap. | ||
452 | */ | ||
453 | function rootLevel() { | ||
454 | $this->ncx->rootLevel(); | ||
455 | } | ||
456 | |||
457 | /** | ||
458 | * Step back to the given level. | ||
459 | * Useful for returning to a previous level from deep within the structure. | ||
460 | * Values below 2 will have the same effect as rootLevel() | ||
461 | * | ||
462 | * @param int $newLevel | ||
463 | */ | ||
464 | function setCurrentLevel($newLevel) { | ||
465 | $this->ncx->setCurrentLevel($newLevel); | ||
466 | } | ||
467 | |||
468 | /** | ||
469 | * Get current level count. | ||
470 | * The indentation of the current structure point. | ||
471 | * | ||
472 | * @return current level count; | ||
473 | */ | ||
474 | function getCurrentLevel() { | ||
475 | return $this->ncx->getCurrentLevel(); | ||
476 | } | ||
477 | |||
478 | /** | ||
479 | * Wrap ChapterContent with Head and Footer | ||
480 | * | ||
481 | * @param $content | ||
482 | * @return string $content | ||
483 | */ | ||
484 | private function wrapChapter($content) { | ||
485 | return $this->htmlContentHeader . "\n" . $content . "\n" . $this->htmlContentFooter; | ||
486 | } | ||
487 | |||
488 | /** | ||
489 | * Reference pages is usually one or two pages for items such as Table of Contents, reference lists, Author notes or Acknowledgements. | ||
490 | * These do not show up in the regular navigation list. | ||
491 | * | ||
492 | * As they are supposed to be short. | ||
493 | * | ||
494 | * @param string $pageName Name of the chapter, will be use din the TOC | ||
495 | * @param string $fileName Filename to use for the chapter, must be unique for the book. | ||
496 | * @param string $pageData Page content in XHTML. File should NOT exceed 250kB. | ||
497 | * @param string $reference Reference key | ||
498 | * @param int $externalReferences How to handle external references. See documentation for <code>processChapterExternalReferences</code> for explanation. Default is EPub::EXTERNAL_REF_IGNORE. | ||
499 | * @param string $baseDir Default is "", meaning it is pointing to the document root. NOT used if $externalReferences is set to EPub::EXTERNAL_REF_IGNORE. | ||
500 | * @return bool $success | ||
501 | */ | ||
502 | function addReferencePage($pageName, $fileName, $pageData, $reference, $externalReferences = EPub::EXTERNAL_REF_IGNORE, $baseDir = "") { | ||
503 | if ($this->isFinalized) { | ||
504 | return FALSE; | ||
505 | } | ||
506 | $fileName = Zip::getRelativePath($fileName); | ||
507 | $fileName = preg_replace('#^[/\.]+#i', "", $fileName); | ||
508 | |||
509 | |||
510 | if (!empty($pageData) && is_string($pageData)) { | ||
511 | if ($this->encodeHTML === TRUE) { | ||
512 | $pageData = $this->encodeHtml($pageData); | ||
513 | } | ||
514 | |||
515 | $this->wrapChapter($pageData); | ||
516 | |||
517 | if ($externalReferences !== EPub::EXTERNAL_REF_IGNORE) { | ||
518 | $htmlDirInfo = pathinfo($fileName); | ||
519 | $htmlDir = preg_replace('#^[/\.]+#i', "", $htmlDirInfo["dirname"] . "/"); | ||
520 | $this->processChapterExternalReferences($pageData, $externalReferences, $baseDir, $htmlDir); | ||
521 | } | ||
522 | |||
523 | $this->addFile($fileName, "ref_" . $reference, $pageData, "application/xhtml+xml"); | ||
524 | |||
525 | if ($reference !== Reference::TABLE_OF_CONTENTS || !isset($this->ncx->referencesList[$reference])) { | ||
526 | $this->opf->addItemRef("ref_" . $reference, FALSE); | ||
527 | $this->opf->addReference($reference, $pageName, $fileName); | ||
528 | |||
529 | $this->ncx->referencesList[$reference] = $fileName; | ||
530 | $this->ncx->referencesName[$reference] = $pageName; | ||
531 | } | ||
532 | return TRUE; | ||
533 | } | ||
534 | return TRUE; | ||
535 | } | ||
536 | |||
537 | /** | ||
538 | * Add custom metadata to the book. | ||
539 | * | ||
540 | * It is up to the builder to make sure there are no collisions. Metadata are just key value pairs. | ||
541 | * | ||
542 | * @param string $name | ||
543 | * @param string $content | ||
544 | */ | ||
545 | function addCustomMetadata($name, $content) { | ||
546 | $this->opf->addMeta($name, $content); | ||
547 | } | ||
548 | |||
549 | /** | ||
550 | * Add DublinCore metadata to the book | ||
551 | * | ||
552 | * Use the DublinCore constants included in EPub, ie DublinCore::DATE | ||
553 | * | ||
554 | * @param string $dublinCore name | ||
555 | * @param string $value | ||
556 | */ | ||
557 | function addDublinCoreMetadata($dublinCoreConstant, $value) { | ||
558 | if ($this->isFinalized) { | ||
559 | return; | ||
560 | } | ||
561 | |||
562 | $this->opf->addDCMeta($dublinCoreConstant, $this->decodeHtmlEntities($value)); | ||
563 | } | ||
564 | |||
565 | /** | ||
566 | * Add a cover image to the book. | ||
567 | * If the $imageData is not set, the function assumes the $fileName is the path to the image file. | ||
568 | * | ||
569 | * The styling and structure of the generated XHTML is heavily inspired by the XHTML generated by Calibre. | ||
570 | * | ||
571 | * @param string $fileName Filename to use for the image, must be unique for the book. | ||
572 | * @param string $imageData Binary image data | ||
573 | * @param string $mimetype Image mimetype, such as "image/jpeg" or "image/png". | ||
574 | * @return bool $success | ||
575 | */ | ||
576 | function setCoverImage($fileName, $imageData = NULL, $mimetype = NULL) { | ||
577 | if ($this->isFinalized || $this->isCoverImageSet || array_key_exists("CoverPage.html", $this->fileList)) { | ||
578 | return FALSE; | ||
579 | } | ||
580 | |||
581 | if ($imageData == NULL) { | ||
582 | // assume $fileName is the valid file path. | ||
583 | if (!file_exists($fileName)) { | ||
584 | // Attempt to locate the file using the doc root. | ||
585 | $rp = realpath($this->docRoot . "/" . $fileName); | ||
586 | |||
587 | if ($rp !== FALSE) { | ||
588 | // only assign the docroot path if it actually exists there. | ||
589 | $fileName = $rp; | ||
590 | } | ||
591 | } | ||
592 | $image = $this->getImage($fileName); | ||
593 | $imageData = $image['image']; | ||
594 | $mimetype = $image['mime']; | ||
595 | $fileName = preg_replace("#\.[^\.]+$#", "." . $image['ext'], $fileName); | ||
596 | } | ||
597 | |||
598 | |||
599 | $path = pathinfo($fileName); | ||
600 | $imgPath = "images/" . $path["basename"]; | ||
601 | |||
602 | if (empty($mimetype) && file_exists($fileName)) { | ||
603 | list($width, $height, $type, $attr) = getimagesize($fileName); | ||
604 | $mimetype = image_type_to_mime_type($type); | ||
605 | } | ||
606 | if (empty($mimetype)) { | ||
607 | $ext = strtolower($path['extension']); | ||
608 | if ($ext == "jpg") { | ||
609 | $ext = "jpeg"; | ||
610 | } | ||
611 | $mimetype = "image/" . $ext; | ||
612 | } | ||
613 | |||
614 | $coverPage = ""; | ||
615 | |||
616 | if ($this->isEPubVersion2()) { | ||
617 | $coverPage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" | ||
618 | . "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n" | ||
619 | . " \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n" | ||
620 | . "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\" xml:lang=\"en\">\n" | ||
621 | . "\t<head>\n" | ||
622 | . "\t\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n" | ||
623 | . "\t\t<title>Cover Image</title>\n" | ||
624 | . "\t\t<link type=\"text/css\" rel=\"stylesheet\" href=\"Styles/CoverPage.css\" />\n" | ||
625 | . "\t</head>\n" | ||
626 | . "\t<body>\n" | ||
627 | . "\t\t<div>\n" | ||
628 | . "\t\t\t<img src=\"" . $imgPath . "\" alt=\"Cover image\" style=\"height: 100%\"/>\n" | ||
629 | . "\t\t</div>\n" | ||
630 | . "\t</body>\n" | ||
631 | . "</html>\n"; | ||
632 | } else { | ||
633 | $coverPage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" | ||
634 | . "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n" | ||
635 | . "<head>" | ||
636 | . "\t<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n" | ||
637 | . "\t\t<title>Cover Image</title>\n" | ||
638 | . "\t\t<link type=\"text/css\" rel=\"stylesheet\" href=\"Styles/CoverPage.css\" />\n" | ||
639 | . "\t</head>\n" | ||
640 | . "\t<body>\n" | ||
641 | . "\t\t<section epub:type=\"cover\">\n" | ||
642 | . "\t\t\t<img src=\"" . $imgPath . "\" alt=\"Cover image\" style=\"height: 100%\"/>\n" | ||
643 | . "\t\t</section>\n" | ||
644 | . "\t</body>\n" | ||
645 | . "</html>\n"; | ||
646 | } | ||
647 | $coverPageCss = "@page, body, div, img {\n" | ||
648 | . "\tpadding: 0pt;\n" | ||
649 | . "\tmargin:0pt;\n" | ||
650 | . "}\n\nbody {\n" | ||
651 | . "\ttext-align: center;\n" | ||
652 | . "}\n"; | ||
653 | |||
654 | $this->addCSSFile("Styles/CoverPage.css", "CoverPageCss", $coverPageCss); | ||
655 | $this->addFile($imgPath, "CoverImage", $imageData, $mimetype); | ||
656 | $this->addReferencePage("CoverPage", "CoverPage.xhtml", $coverPage, "cover"); | ||
657 | $this->isCoverImageSet = TRUE; | ||
658 | return TRUE; | ||
659 | } | ||
660 | |||
661 | /** | ||
662 | * Process external references from a HTML to the book. The chapter itself is not stored. | ||
663 | * the HTML is scanned for <link..., <style..., and <img tags. | ||
664 | * Embedded CSS styles and links will also be processed. | ||
665 | * Script tags are not processed, as scripting should be avoided in e-books. | ||
666 | * | ||
667 | * EPub keeps track of added files, and duplicate files referenced across multiple | ||
668 | * chapters, are only added once. | ||
669 | * | ||
670 | * If the $doc is a string, it is assumed to be the content of an HTML file, | ||
671 | * else is it assumes to be a DOMDocument. | ||
672 | * | ||
673 | * Basedir is the root dir the HTML is supposed to "live" in, used to resolve | ||
674 | * relative references such as <code><img src="../images/image.png"/></code> | ||
675 | * | ||
676 | * $externalReferences determines how the function will handle external references. | ||
677 | * | ||
678 | * @param mixed &$doc (referenced) | ||
679 | * @param int $externalReferences How to handle external references, EPub::EXTERNAL_REF_IGNORE, EPub::EXTERNAL_REF_ADD or EPub::EXTERNAL_REF_REMOVE_IMAGES? Default is EPub::EXTERNAL_REF_ADD. | ||
680 | * @param string $baseDir Default is "", meaning it is pointing to the document root. | ||
681 | * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive. | ||
682 | * | ||
683 | * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE). | ||
684 | */ | ||
685 | protected function processChapterExternalReferences(&$doc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "") { | ||
686 | if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) { | ||
687 | return FALSE; | ||
688 | } | ||
689 | |||
690 | $backPath = preg_replace('#[^/]+/#i', "../", $htmlDir); | ||
691 | $isDocAString = is_string($doc); | ||
692 | $xmlDoc = NULL; | ||
693 | |||
694 | if ($isDocAString) { | ||
695 | $xmlDoc = new DOMDocument(); | ||
696 | @$xmlDoc->loadHTML($doc); | ||
697 | } else { | ||
698 | $xmlDoc = $doc; | ||
699 | } | ||
700 | |||
701 | $this->processChapterStyles($xmlDoc, $externalReferences, $baseDir, $htmlDir); | ||
702 | $this->processChapterLinks($xmlDoc, $externalReferences, $baseDir, $htmlDir, $backPath); | ||
703 | $this->processChapterImages($xmlDoc, $externalReferences, $baseDir, $htmlDir, $backPath); | ||
704 | $this->processChapterSources($xmlDoc, $externalReferences, $baseDir, $htmlDir, $backPath); | ||
705 | |||
706 | if ($isDocAString) { | ||
707 | //$html = $xmlDoc->saveXML(); | ||
708 | |||
709 | $htmlNode = $xmlDoc->getElementsByTagName("html"); | ||
710 | $headNode = $xmlDoc->getElementsByTagName("head"); | ||
711 | $bodyNode = $xmlDoc->getElementsByTagName("body"); | ||
712 | |||
713 | $htmlNS = ""; | ||
714 | for ($index = 0; $index < $htmlNode->item(0)->attributes->length; $index++) { | ||
715 | $nodeName = $htmlNode->item(0)->attributes->item($index)->nodeName; | ||
716 | $nodeValue = $htmlNode->item(0)->attributes->item($index)->nodeValue; | ||
717 | |||
718 | if ($nodeName != "xmlns") { | ||
719 | $htmlNS .= " $nodeName=\"$nodeValue\""; | ||
720 | } | ||
721 | } | ||
722 | |||
723 | $xml = new DOMDocument('1.0', "utf-8"); | ||
724 | $xml->lookupPrefix("http://www.w3.org/1999/xhtml"); | ||
725 | $xml->preserveWhiteSpace = FALSE; | ||
726 | $xml->formatOutput = TRUE; | ||
727 | |||
728 | $xml2Doc = new DOMDocument('1.0', "utf-8"); | ||
729 | $xml2Doc->lookupPrefix("http://www.w3.org/1999/xhtml"); | ||
730 | $xml2Doc->loadXML("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\"$htmlNS>\n</html>\n"); | ||
731 | $html = $xml2Doc->getElementsByTagName("html")->item(0); | ||
732 | $html->appendChild($xml2Doc->importNode($headNode->item(0), TRUE)); | ||
733 | $html->appendChild($xml2Doc->importNode($bodyNode->item(0), TRUE)); | ||
734 | |||
735 | // force pretty printing and correct formatting, should not be needed, but it is. | ||
736 | $xml->loadXML($xml2Doc->saveXML()); | ||
737 | $doc = $xml->saveXML(); | ||
738 | |||
739 | if (!$this->isEPubVersion2()) { | ||
740 | $doc = preg_replace('#^\s*<!DOCTYPE\ .+?>\s*#im', '', $doc); | ||
741 | } | ||
742 | } | ||
743 | return TRUE; | ||
744 | } | ||
745 | |||
746 | /** | ||
747 | * Process images referenced from an CSS file to the book. | ||
748 | * | ||
749 | * $externalReferences determins how the function will handle external references. | ||
750 | * | ||
751 | * @param string &$cssFile (referenced) | ||
752 | * @param int $externalReferences How to handle external references, EPub::EXTERNAL_REF_IGNORE, EPub::EXTERNAL_REF_ADD or EPub::EXTERNAL_REF_REMOVE_IMAGES? Default is EPub::EXTERNAL_REF_ADD. | ||
753 | * @param string $baseDir Default is "", meaning it is pointing to the document root. | ||
754 | * @param string $cssDir The of the CSS file's directory from the root of the archive. | ||
755 | * | ||
756 | * @return bool FALSE if unsuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE). | ||
757 | */ | ||
758 | protected function processCSSExternalReferences(&$cssFile, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $cssDir = "") { | ||
759 | if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) { | ||
760 | return FALSE; | ||
761 | } | ||
762 | |||
763 | $backPath = preg_replace('#[^/]+/#i', "../", $cssDir); | ||
764 | $imgs = null; | ||
765 | preg_match_all('#url\s*\([\'\"\s]*(.+?)[\'\"\s]*\)#im', $cssFile, $imgs, PREG_SET_ORDER); | ||
766 | |||
767 | $itemCount = count($imgs); | ||
768 | for ($idx = 0; $idx < $itemCount; $idx++) { | ||
769 | $img = $imgs[$idx]; | ||
770 | if ($externalReferences === EPub::EXTERNAL_REF_REMOVE_IMAGES || $externalReferences === EPub::EXTERNAL_REF_REPLACE_IMAGES) { | ||
771 | $cssFile = str_replace($img[0], "", $cssFile); | ||
772 | } else { | ||
773 | $source = $img[1]; | ||
774 | |||
775 | $pathData = pathinfo($source); | ||
776 | $internalSrc = $pathData['basename']; | ||
777 | $internalPath = ""; | ||
778 | $isSourceExternal = FALSE; | ||
779 | |||
780 | if ($this->resolveImage($source, $internalPath, $internalSrc, $isSourceExternal, $baseDir, $cssDir, $backPath)) { | ||
781 | $cssFile = str_replace($img[0], "url('" . $backPath . $internalPath . "')", $cssFile); | ||
782 | } else if ($isSourceExternal) { | ||
783 | $cssFile = str_replace($img[0], "", $cssFile); // External image is missing | ||
784 | } // else do nothing, if the image is local, and missing, assume it's been generated. | ||
785 | } | ||
786 | } | ||
787 | return TRUE; | ||
788 | } | ||
789 | |||
790 | /** | ||
791 | * Process style tags in a DOMDocument. Styles will be passed as CSS files and reinserted into the document. | ||
792 | * | ||
793 | * @param DOMDocument &$xmlDoc (referenced) | ||
794 | * @param int $externalReferences How to handle external references, EPub::EXTERNAL_REF_IGNORE, EPub::EXTERNAL_REF_ADD or EPub::EXTERNAL_REF_REMOVE_IMAGES? Default is EPub::EXTERNAL_REF_ADD. | ||
795 | * @param string $baseDir Default is "", meaning it is pointing to the document root. | ||
796 | * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive. | ||
797 | * | ||
798 | * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE). | ||
799 | */ | ||
800 | protected function processChapterStyles(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "") { | ||
801 | if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) { | ||
802 | return FALSE; | ||
803 | } | ||
804 | // process inlined CSS styles in style tags. | ||
805 | $styles = $xmlDoc->getElementsByTagName("style"); | ||
806 | $styleCount = $styles->length; | ||
807 | for ($styleIdx = 0; $styleIdx < $styleCount; $styleIdx++) { | ||
808 | $style = $styles->item($styleIdx); | ||
809 | |||
810 | $styleData = preg_replace('#[/\*\s]*\<\!\[CDATA\[[\s\*/]*#im', "", $style->nodeValue); | ||
811 | $styleData = preg_replace('#[/\*\s]*\]\]\>[\s\*/]*#im', "", $styleData); | ||
812 | |||
813 | $this->processCSSExternalReferences($styleData, $externalReferences, $baseDir, $htmlDir); | ||
814 | $style->nodeValue = "\n" . trim($styleData) . "\n"; | ||
815 | } | ||
816 | return TRUE; | ||
817 | } | ||
818 | |||
819 | /** | ||
820 | * Process link tags in a DOMDocument. Linked files will be loaded into the archive, and the link src will be rewritten to point to that location. | ||
821 | * Link types text/css will be passed as CSS files. | ||
822 | * | ||
823 | * @param DOMDocument &$xmlDoc (referenced) | ||
824 | * @param int $externalReferences How to handle external references, EPub::EXTERNAL_REF_IGNORE, EPub::EXTERNAL_REF_ADD or EPub::EXTERNAL_REF_REMOVE_IMAGES? Default is EPub::EXTERNAL_REF_ADD. | ||
825 | * @param string $baseDir Default is "", meaning it is pointing to the document root. | ||
826 | * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive. | ||
827 | * @param string $backPath The path to get back to the root of the archive from $htmlDir. | ||
828 | * | ||
829 | * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE). | ||
830 | */ | ||
831 | protected function processChapterLinks(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "", $backPath = "") { | ||
832 | if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) { | ||
833 | return FALSE; | ||
834 | } | ||
835 | // process link tags. | ||
836 | $links = $xmlDoc->getElementsByTagName("link"); | ||
837 | $linkCount = $links->length; | ||
838 | for ($linkIdx = 0; $linkIdx < $linkCount; $linkIdx++) { | ||
839 | $link = $links->item($linkIdx); | ||
840 | $source = $link->attributes->getNamedItem("href")->nodeValue; | ||
841 | $sourceData = NULL; | ||
842 | |||
843 | $pathData = pathinfo($source); | ||
844 | $internalSrc = $pathData['basename']; | ||
845 | |||
846 | if (preg_match('#^(http|ftp)s?://#i', $source) == 1) { | ||
847 | $urlinfo = parse_url($source); | ||
848 | |||
849 | if (strpos($urlinfo['path'], $baseDir."/") !== FALSE) { | ||
850 | $internalSrc = substr($urlinfo['path'], strpos($urlinfo['path'], $baseDir."/") + strlen($baseDir) + 1); | ||
851 | } | ||
852 | |||
853 | @$sourceData = getFileContents($source); | ||
854 | } else if (strpos($source, "/") === 0) { | ||
855 | @$sourceData = file_get_contents($this->docRoot . $source); | ||
856 | } else { | ||
857 | @$sourceData = file_get_contents($this->docRoot . $baseDir . "/" . $source); | ||
858 | } | ||
859 | |||
860 | if (!empty($sourceData)) { | ||
861 | if (!array_key_exists($internalSrc, $this->fileList)) { | ||
862 | $mime = $link->attributes->getNamedItem("type")->nodeValue; | ||
863 | if (empty($mime)) { | ||
864 | $mime = "text/plain"; | ||
865 | } | ||
866 | if ($mime == "text/css") { | ||
867 | $this->processCSSExternalReferences($sourceData, $externalReferences, $baseDir, $htmlDir); | ||
868 | $this->addCSSFile($internalSrc, $internalSrc, $sourceData, EPub::EXTERNAL_REF_IGNORE, $baseDir); | ||
869 | $link->setAttribute("href", $backPath . $internalSrc); | ||
870 | } else { | ||
871 | $this->addFile($internalSrc, $internalSrc, $sourceData, $mime); | ||
872 | } | ||
873 | $this->fileList[$internalSrc] = $source; | ||
874 | } else { | ||
875 | $link->setAttribute("href", $backPath . $internalSrc); | ||
876 | } | ||
877 | } // else do nothing, if the link is local, and missing, assume it's been generated. | ||
878 | } | ||
879 | return TRUE; | ||
880 | } | ||
881 | |||
882 | /** | ||
883 | * Process img tags in a DOMDocument. | ||
884 | * $externalReferences will determine what will happen to these images, and the img src will be rewritten accordingly. | ||
885 | * | ||
886 | * @param DOMDocument &$xmlDoc (referenced) | ||
887 | * @param int $externalReferences How to handle external references, EPub::EXTERNAL_REF_IGNORE, EPub::EXTERNAL_REF_ADD or EPub::EXTERNAL_REF_REMOVE_IMAGES? Default is EPub::EXTERNAL_REF_ADD. | ||
888 | * @param string $baseDir Default is "", meaning it is pointing to the document root. | ||
889 | * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive. | ||
890 | * @param string $backPath The path to get back to the root of the archive from $htmlDir. | ||
891 | * | ||
892 | * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE). | ||
893 | */ | ||
894 | protected function processChapterImages(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "", $backPath = "") { | ||
895 | if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) { | ||
896 | return FALSE; | ||
897 | } | ||
898 | // process img tags. | ||
899 | $postProcDomElememts = array(); | ||
900 | $images = $xmlDoc->getElementsByTagName("img"); | ||
901 | $itemCount = $images->length; | ||
902 | |||
903 | for ($idx = 0; $idx < $itemCount; $idx++) { | ||
904 | $img = $images->item($idx); | ||
905 | |||
906 | if ($externalReferences === EPub::EXTERNAL_REF_REMOVE_IMAGES) { | ||
907 | $postProcDomElememts[] = $img; | ||
908 | } else if ($externalReferences === EPub::EXTERNAL_REF_REPLACE_IMAGES) { | ||
909 | $altNode = $img->attributes->getNamedItem("alt"); | ||
910 | $alt = "image"; | ||
911 | if ($altNode !== NULL && strlen($altNode->nodeValue) > 0) { | ||
912 | $alt = $altNode->nodeValue; | ||
913 | } | ||
914 | $postProcDomElememts[] = array($img, $this->createDomFragment($xmlDoc, "<em>[" . $alt . "]</em>")); | ||
915 | } else { | ||
916 | $source = $img->attributes->getNamedItem("src")->nodeValue; | ||
917 | |||
918 | $parsedSource = parse_url($source); | ||
919 | $internalSrc = $this->sanitizeFileName(urldecode(pathinfo($parsedSource['path'], PATHINFO_BASENAME))); | ||
920 | $internalPath = ""; | ||
921 | $isSourceExternal = FALSE; | ||
922 | |||
923 | if ($this->resolveImage($source, $internalPath, $internalSrc, $isSourceExternal, $baseDir, $htmlDir, $backPath)) { | ||
924 | $img->setAttribute("src", $backPath . $internalPath); | ||
925 | } else if ($isSourceExternal) { | ||
926 | $postProcDomElememts[] = $img; // External image is missing | ||
927 | } // else do nothing, if the image is local, and missing, assume it's been generated. | ||
928 | } | ||
929 | } | ||
930 | |||
931 | foreach ($postProcDomElememts as $target) { | ||
932 | if (is_array($target)) { | ||
933 | $target[0]->parentNode->replaceChild($target[1], $target[0]); | ||
934 | } else { | ||
935 | $target->parentNode->removeChild($target); | ||
936 | } | ||
937 | } | ||
938 | return TRUE; | ||
939 | } | ||
940 | |||
941 | /** | ||
942 | * Process source tags in a DOMDocument. | ||
943 | * $externalReferences will determine what will happen to these images, and the img src will be rewritten accordingly. | ||
944 | * | ||
945 | * @param DOMDocument &$xmlDoc (referenced) | ||
946 | * @param int $externalReferences How to handle external references, EPub::EXTERNAL_REF_IGNORE, EPub::EXTERNAL_REF_ADD or EPub::EXTERNAL_REF_REMOVE_IMAGES? Default is EPub::EXTERNAL_REF_ADD. | ||
947 | * @param string $baseDir Default is "", meaning it is pointing to the document root. | ||
948 | * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive. | ||
949 | * @param string $backPath The path to get back to the root of the archive from $htmlDir. | ||
950 | * | ||
951 | * @return bool FALSE if uncuccessful (book is finalized or $externalReferences == EXTERNAL_REF_IGNORE). | ||
952 | */ | ||
953 | protected function processChapterSources(&$xmlDoc, $externalReferences = EPub::EXTERNAL_REF_ADD, $baseDir = "", $htmlDir = "", $backPath = "") { | ||
954 | if ($this->isFinalized || $externalReferences === EPub::EXTERNAL_REF_IGNORE) { | ||
955 | return FALSE; | ||
956 | } | ||
957 | |||
958 | if ($this->bookVersion !== EPub::BOOK_VERSION_EPUB3) { | ||
959 | // ePub 2 does not support multimedia formats, and they must be removed. | ||
960 | $externalReferences = EPub::EXTERNAL_REF_REMOVE_IMAGES; | ||
961 | } | ||
962 | |||
963 | $postProcDomElememts = array(); | ||
964 | $images = $xmlDoc->getElementsByTagName("source"); | ||
965 | $itemCount = $images->length; | ||
966 | for ($idx = 0; $idx < $itemCount; $idx++) { | ||
967 | $img = $images->item($idx); | ||
968 | if ($externalReferences === EPub::EXTERNAL_REF_REMOVE_IMAGES) { | ||
969 | $postProcDomElememts[] = $img; | ||
970 | } else if ($externalReferences === EPub::EXTERNAL_REF_REPLACE_IMAGES) { | ||
971 | $altNode = $img->attributes->getNamedItem("alt"); | ||
972 | $alt = "image"; | ||
973 | if ($altNode !== NULL && strlen($altNode->nodeValue) > 0) { | ||
974 | $alt = $altNode->nodeValue; | ||
975 | } | ||
976 | $postProcDomElememts[] = array($img, $this->createDomFragment($xmlDoc, "[" . $alt . "]")); | ||
977 | } else { | ||
978 | $source = $img->attributes->getNamedItem("src")->nodeValue; | ||
979 | |||
980 | $parsedSource = parse_url($source); | ||
981 | $internalSrc = $this->sanitizeFileName(urldecode(pathinfo($parsedSource['path'], PATHINFO_BASENAME))); | ||
982 | $internalPath = ""; | ||
983 | $isSourceExternal = FALSE; | ||
984 | |||
985 | if ($this->resolveMedia($source, $internalPath, $internalSrc, $isSourceExternal, $baseDir, $htmlDir, $backPath)) { | ||
986 | $img->setAttribute("src", $backPath . $internalPath); | ||
987 | } else if ($isSourceExternal) { | ||
988 | $postProcDomElememts[] = $img; // External image is missing | ||
989 | } // else do nothing, if the image is local, and missing, assume it's been generated. | ||
990 | } | ||
991 | } | ||
992 | } | ||
993 | |||
994 | /** | ||
995 | * Resolve an image src and determine it's target location and add it to the book. | ||
996 | * | ||
997 | * @param string $source Image Source link. | ||
998 | * @param string &$internalPath (referenced) Return value, will be set to the target path and name in the book. | ||
999 | * @param string &$internalSrc (referenced) Return value, will be set to the target name in the book. | ||
1000 | * @param string &$isSourceExternal (referenced) Return value, will be set to TRUE if the image originated from a full URL. | ||
1001 | * @param string $baseDir Default is "", meaning it is pointing to the document root. | ||
1002 | * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive. | ||
1003 | * @param string $backPath The path to get back to the root of the archive from $htmlDir. | ||
1004 | */ | ||
1005 | protected function resolveImage($source, &$internalPath, &$internalSrc, &$isSourceExternal, $baseDir = "", $htmlDir = "", $backPath = "") { | ||
1006 | if ($this->isFinalized) { | ||
1007 | return FALSE; | ||
1008 | } | ||
1009 | $imageData = NULL; | ||
1010 | |||
1011 | if (preg_match('#^(http|ftp)s?://#i', $source) == 1) { | ||
1012 | $urlinfo = parse_url($source); | ||
1013 | $urlPath = pathinfo($urlinfo['path']); | ||
1014 | |||
1015 | if (strpos($urlinfo['path'], $baseDir."/") !== FALSE) { | ||
1016 | $internalSrc = $this->sanitizeFileName(urldecode(substr($urlinfo['path'], strpos($urlinfo['path'], $baseDir."/") + strlen($baseDir) + 1))); | ||
1017 | } | ||
1018 | $internalPath = $urlinfo["scheme"] . "/" . $urlinfo["host"] . "/" . pathinfo($urlinfo["path"], PATHINFO_DIRNAME); | ||
1019 | $isSourceExternal = TRUE; | ||
1020 | $imageData = $this->getImage($source); | ||
1021 | } else if (strpos($source, "/") === 0) { | ||
1022 | $internalPath = pathinfo($source, PATHINFO_DIRNAME); | ||
1023 | |||
1024 | $path = $source; | ||
1025 | if (!file_exists($path)) { | ||
1026 | $path = $this->docRoot . $path; | ||
1027 | } | ||
1028 | |||
1029 | $imageData = $this->getImage($path); | ||
1030 | } else { | ||
1031 | $internalPath = $htmlDir . "/" . preg_replace('#^[/\.]+#', '', pathinfo($source, PATHINFO_DIRNAME)); | ||
1032 | |||
1033 | $path = $baseDir . "/" . $source; | ||
1034 | if (!file_exists($path)) { | ||
1035 | $path = $this->docRoot . $path; | ||
1036 | } | ||
1037 | |||
1038 | $imageData = $this->getImage($path); | ||
1039 | } | ||
1040 | if ($imageData !== FALSE) { | ||
1041 | $iSrcInfo = pathinfo($internalSrc); | ||
1042 | if (!empty($imageData['ext']) && $imageData['ext'] != $iSrcInfo['extension']) { | ||
1043 | $internalSrc = $iSrcInfo['filename'] . "." . $imageData['ext']; | ||
1044 | } | ||
1045 | $internalPath = Zip::getRelativePath("images/" . $internalPath . "/" . $internalSrc); | ||
1046 | if (!array_key_exists($internalPath, $this->fileList)) { | ||
1047 | $this->addFile($internalPath, "i_" . $internalSrc, $imageData['image'], $imageData['mime']); | ||
1048 | $this->fileList[$internalPath] = $source; | ||
1049 | } | ||
1050 | return TRUE; | ||
1051 | } | ||
1052 | return FALSE; | ||
1053 | } | ||
1054 | |||
1055 | /** | ||
1056 | * Resolve a media src and determine it's target location and add it to the book. | ||
1057 | * | ||
1058 | * @param string $source Source link. | ||
1059 | * @param string $internalPath (referenced) Return value, will be set to the target path and name in the book. | ||
1060 | * @param string $internalSrc (referenced) Return value, will be set to the target name in the book. | ||
1061 | * @param string $isSourceExternal (referenced) Return value, will be set to TRUE if the image originated from a full URL. | ||
1062 | * @param string $baseDir Default is "", meaning it is pointing to the document root. | ||
1063 | * @param string $htmlDir The path to the parent HTML file's directory from the root of the archive. | ||
1064 | * @param string $backPath The path to get back to the root of the archive from $htmlDir. | ||
1065 | */ | ||
1066 | protected function resolveMedia($source, &$internalPath, &$internalSrc, &$isSourceExternal, $baseDir = "", $htmlDir = "", $backPath = "") { | ||
1067 | if ($this->isFinalized) { | ||
1068 | return FALSE; | ||
1069 | } | ||
1070 | $mediaPath = NULL; | ||
1071 | $tmpFile; | ||
1072 | |||
1073 | if (preg_match('#^(http|ftp)s?://#i', $source) == 1) { | ||
1074 | $urlinfo = parse_url($source); | ||
1075 | |||
1076 | if (strpos($urlinfo['path'], $baseDir."/") !== FALSE) { | ||
1077 | $internalSrc = substr($urlinfo['path'], strpos($urlinfo['path'], $baseDir."/") + strlen($baseDir) + 1); | ||
1078 | } | ||
1079 | $internalPath = $urlinfo["scheme"] . "/" . $urlinfo["host"] . "/" . pathinfo($urlinfo["path"], PATHINFO_DIRNAME); | ||
1080 | $isSourceExternal = TRUE; | ||
1081 | $mediaPath = $this->getFileContents($source, true); | ||
1082 | $tmpFile = $mediaPath; | ||
1083 | } else if (strpos($source, "/") === 0) { | ||
1084 | $internalPath = pathinfo($source, PATHINFO_DIRNAME); | ||
1085 | |||
1086 | $mediaPath = $source; | ||
1087 | if (!file_exists($mediaPath)) { | ||
1088 | $mediaPath = $this->docRoot . $mediaPath; | ||
1089 | } | ||
1090 | } else { | ||
1091 | $internalPath = $htmlDir . "/" . preg_replace('#^[/\.]+#', '', pathinfo($source, PATHINFO_DIRNAME)); | ||
1092 | |||
1093 | $mediaPath = $baseDir . "/" . $source; | ||
1094 | if (!file_exists($mediaPath)) { | ||
1095 | $mediaPath = $this->docRoot . $mediaPath; | ||
1096 | } | ||
1097 | } | ||
1098 | |||
1099 | if ($mediaPath !== FALSE) { | ||
1100 | $mime = $this->getMime($source); | ||
1101 | $internalPath = Zip::getRelativePath("media/" . $internalPath . "/" . $internalSrc); | ||
1102 | |||
1103 | if (!array_key_exists($internalPath, $this->fileList) && | ||
1104 | $this->addLargeFile($internalPath, "m_" . $internalSrc, $mediaPath, $mime)) { | ||
1105 | $this->fileList[$internalPath] = $source; | ||
1106 | } | ||
1107 | if (isset($tmpFile)) { | ||
1108 | unlink($tmpFile); | ||
1109 | } | ||
1110 | return TRUE; | ||
1111 | } | ||
1112 | return FALSE; | ||
1113 | } | ||
1114 | |||
1115 | /** | ||
1116 | * Get Book Chapter count. | ||
1117 | * | ||
1118 | * @access public | ||
1119 | * @return number of chapters | ||
1120 | */ | ||
1121 | function getChapterCount() { | ||
1122 | return $this->chapterCount; | ||
1123 | } | ||
1124 | |||
1125 | /** | ||
1126 | * Book title, mandatory. | ||
1127 | * | ||
1128 | * Used for the dc:title metadata parameter in the OPF file as well as the DocTitle attribute in the NCX file. | ||
1129 | * | ||
1130 | * @param string $title | ||
1131 | * @access public | ||
1132 | * @return bool $success | ||
1133 | */ | ||
1134 | function setTitle($title) { | ||
1135 | if ($this->isFinalized) { | ||
1136 | return FALSE; | ||
1137 | } | ||
1138 | $this->title = $title; | ||
1139 | return TRUE; | ||
1140 | } | ||
1141 | |||
1142 | /** | ||
1143 | * Get Book title. | ||
1144 | * | ||
1145 | * @access public | ||
1146 | * @return $title | ||
1147 | */ | ||
1148 | function getTitle() { | ||
1149 | return $this->title; | ||
1150 | } | ||
1151 | |||
1152 | /** | ||
1153 | * Book language, mandatory | ||
1154 | * | ||
1155 | * Use the RFC3066 Language codes, such as "en", "da", "fr" etc. | ||
1156 | * Defaults to "en". | ||
1157 | * | ||
1158 | * Used for the dc:language metadata parameter in the OPF file. | ||
1159 | * | ||
1160 | * @param string $language | ||
1161 | * @access public | ||
1162 | * @return bool $success | ||
1163 | */ | ||
1164 | function setLanguage($language) { | ||
1165 | if ($this->isFinalized || mb_strlen($language) != 2) { | ||
1166 | return FALSE; | ||
1167 | } | ||
1168 | $this->language = $language; | ||
1169 | return TRUE; | ||
1170 | } | ||
1171 | |||
1172 | /** | ||
1173 | * Get Book language. | ||
1174 | * | ||
1175 | * @access public | ||
1176 | * @return $language | ||
1177 | */ | ||
1178 | function getLanguage() { | ||
1179 | return $this->language; | ||
1180 | } | ||
1181 | |||
1182 | /** | ||
1183 | * Unique book identifier, mandatory. | ||
1184 | * Use the URI, or ISBN if available. | ||
1185 | * | ||
1186 | * An unambiguous reference to the resource within a given context. | ||
1187 | * | ||
1188 | * Recommended best practice is to identify the resource by means of a | ||
1189 | * string conforming to a formal identification system. | ||
1190 | * | ||
1191 | * Used for the dc:identifier metadata parameter in the OPF file, as well | ||
1192 | * as dtb:uid in the NCX file. | ||
1193 | * | ||
1194 | * Identifier type should only be: | ||
1195 | * EPub::IDENTIFIER_URI | ||
1196 | * EPub::IDENTIFIER_ISBN | ||
1197 | * EPub::IDENTIFIER_UUID | ||
1198 | * | ||
1199 | * @param string $identifier | ||
1200 | * @param string $identifierType | ||
1201 | * @access public | ||
1202 | * @return bool $success | ||
1203 | */ | ||
1204 | function setIdentifier($identifier, $identifierType) { | ||
1205 | if ($this->isFinalized || ($identifierType !== EPub::IDENTIFIER_URI && $identifierType !== EPub::IDENTIFIER_ISBN && $identifierType !== EPub::IDENTIFIER_UUID)) { | ||
1206 | return FALSE; | ||
1207 | } | ||
1208 | $this->identifier = $identifier; | ||
1209 | $this->identifierType = $identifierType; | ||
1210 | return TRUE; | ||
1211 | } | ||
1212 | |||
1213 | /** | ||
1214 | * Get Book identifier. | ||
1215 | * | ||
1216 | * @access public | ||
1217 | * @return $identifier | ||
1218 | */ | ||
1219 | function getIdentifier() { | ||
1220 | return $this->identifier; | ||
1221 | } | ||
1222 | |||
1223 | /** | ||
1224 | * Get Book identifierType. | ||
1225 | * | ||
1226 | * @access public | ||
1227 | * @return $identifierType | ||
1228 | */ | ||
1229 | function getIdentifierType() { | ||
1230 | return $this->identifierType; | ||
1231 | } | ||
1232 | |||
1233 | /** | ||
1234 | * Book description, optional. | ||
1235 | * | ||
1236 | * An account of the resource. | ||
1237 | * | ||
1238 | * Description may include but is not limited to: an abstract, a table of | ||
1239 | * contents, a graphical representation, or a free-text account of the | ||
1240 | * resource. | ||
1241 | * | ||
1242 | * Used for the dc:source metadata parameter in the OPF file | ||
1243 | * | ||
1244 | * @param string $description | ||
1245 | * @access public | ||
1246 | * @return bool $success | ||
1247 | */ | ||
1248 | function setDescription($description) { | ||
1249 | if ($this->isFinalized) { | ||
1250 | return FALSE; | ||
1251 | } | ||
1252 | $this->description = $description; | ||
1253 | return TRUE; | ||
1254 | } | ||
1255 | |||
1256 | /** | ||
1257 | * Get Book description. | ||
1258 | * | ||
1259 | * @access public | ||
1260 | * @return $description | ||
1261 | */ | ||
1262 | function getDescription() { | ||
1263 | return $this->description; | ||
1264 | } | ||
1265 | |||
1266 | /** | ||
1267 | * Book author or creator, optional. | ||
1268 | * The $authorSortKey is basically how the name is to be sorted, usually | ||
1269 | * it's "Lastname, First names" where the $author is the straight | ||
1270 | * "Firstnames Lastname" | ||
1271 | * | ||
1272 | * An entity primarily responsible for making the resource. | ||
1273 | * | ||
1274 | * Examples of a Creator include a person, an organization, or a service. | ||
1275 | * Typically, the name of a Creator should be used to indicate the entity. | ||
1276 | * | ||
1277 | * Used for the dc:creator metadata parameter in the OPF file and the | ||
1278 | * docAuthor attribure in the NCX file. | ||
1279 | * The sort key is used for the opf:file-as attribute in dc:creator. | ||
1280 | * | ||
1281 | * @param string $author | ||
1282 | * @param string $authorSortKey | ||
1283 | * @access public | ||
1284 | * @return bool $success | ||
1285 | */ | ||
1286 | function setAuthor($author, $authorSortKey) { | ||
1287 | if ($this->isFinalized) { | ||
1288 | return FALSE; | ||
1289 | } | ||
1290 | $this->author = $author; | ||
1291 | $this->authorSortKey = $authorSortKey; | ||
1292 | return TRUE; | ||
1293 | } | ||
1294 | |||
1295 | /** | ||
1296 | * Get Book author. | ||
1297 | * | ||
1298 | * @access public | ||
1299 | * @return $author | ||
1300 | */ | ||
1301 | function getAuthor() { | ||
1302 | return $this->author; | ||
1303 | } | ||
1304 | |||
1305 | /** | ||
1306 | * Publisher Information, optional. | ||
1307 | * | ||
1308 | * An entity responsible for making the resource available. | ||
1309 | * | ||
1310 | * Examples of a Publisher include a person, an organization, or a service. | ||
1311 | * Typically, the name of a Publisher should be used to indicate the entity. | ||
1312 | * | ||
1313 | * Used for the dc:publisher and dc:relation metadata parameters in the OPF file. | ||
1314 | * | ||
1315 | * @param string $publisherName | ||
1316 | * @param string $publisherURL | ||
1317 | * @access public | ||
1318 | * @return bool $success | ||
1319 | */ | ||
1320 | function setPublisher($publisherName, $publisherURL) { | ||
1321 | if ($this->isFinalized) { | ||
1322 | return FALSE; | ||
1323 | } | ||
1324 | $this->publisherName = $publisherName; | ||
1325 | $this->publisherURL = $publisherURL; | ||
1326 | return TRUE; | ||
1327 | } | ||
1328 | |||
1329 | /** | ||
1330 | * Get Book publisherName. | ||
1331 | * | ||
1332 | * @access public | ||
1333 | * @return $publisherName | ||
1334 | */ | ||
1335 | function getPublisherName() { | ||
1336 | return $this->publisherName; | ||
1337 | } | ||
1338 | |||
1339 | /** | ||
1340 | * Get Book publisherURL. | ||
1341 | * | ||
1342 | * @access public | ||
1343 | * @return $publisherURL | ||
1344 | */ | ||
1345 | function getPublisherURL() { | ||
1346 | return $this->publisherURL; | ||
1347 | } | ||
1348 | |||
1349 | /** | ||
1350 | * Release date, optional. If left blank, the time of the finalization will | ||
1351 | * be used. | ||
1352 | * | ||
1353 | * A point or period of time associated with an event in the lifecycle of | ||
1354 | * the resource. | ||
1355 | * | ||
1356 | * Date may be used to express temporal information at any level of | ||
1357 | * granularity. Recommended best practice is to use an encoding scheme, | ||
1358 | * such as the W3CDTF profile of ISO 8601 [W3CDTF]. | ||
1359 | * | ||
1360 | * Used for the dc:date metadata parameter in the OPF file | ||
1361 | * | ||
1362 | * @param long $timestamp | ||
1363 | * @access public | ||
1364 | * @return bool $success | ||
1365 | */ | ||
1366 | function setDate($timestamp) { | ||
1367 | if ($this->isFinalized) { | ||
1368 | return FALSE; | ||
1369 | } | ||
1370 | $this->date = $timestamp; | ||
1371 | $this->opf->date = $timestamp; | ||
1372 | return TRUE; | ||
1373 | } | ||
1374 | |||
1375 | /** | ||
1376 | * Get Book date. | ||
1377 | * | ||
1378 | * @access public | ||
1379 | * @return $date | ||
1380 | */ | ||
1381 | function getDate() { | ||
1382 | return $this->date; | ||
1383 | } | ||
1384 | |||
1385 | /** | ||
1386 | * Book (copy)rights, optional. | ||
1387 | * | ||
1388 | * Information about rights held in and over the resource. | ||
1389 | * | ||
1390 | * Typically, rights information includes a statement about various | ||
1391 | * property rights associated with the resource, including intellectual | ||
1392 | * property rights. | ||
1393 | * | ||
1394 | * Used for the dc:rights metadata parameter in the OPF file | ||
1395 | * | ||
1396 | * @param string $rightsText | ||
1397 | * @access public | ||
1398 | * @return bool $success | ||
1399 | */ | ||
1400 | function setRights($rightsText) { | ||
1401 | if ($this->isFinalized) { | ||
1402 | return FALSE; | ||
1403 | } | ||
1404 | $this->rights = $rightsText; | ||
1405 | return TRUE; | ||
1406 | } | ||
1407 | |||
1408 | /** | ||
1409 | * Get Book rights. | ||
1410 | * | ||
1411 | * @access public | ||
1412 | * @return $rights | ||
1413 | */ | ||
1414 | function getRights() { | ||
1415 | return $this->rights; | ||
1416 | } | ||
1417 | |||
1418 | /** | ||
1419 | * Add book Subject. | ||
1420 | * | ||
1421 | * The topic of the resource. | ||
1422 | * | ||
1423 | * Typically, the subject will be represented using keywords, key phrases, | ||
1424 | * or classification codes. Recommended best practice is to use a | ||
1425 | * controlled vocabulary. To describe the spatial or temporal topic of the | ||
1426 | * resource, use the Coverage element. | ||
1427 | * | ||
1428 | * @param string $subject | ||
1429 | */ | ||
1430 | function setSubject($subject) { | ||
1431 | if ($this->isFinalized) { | ||
1432 | return; | ||
1433 | } | ||
1434 | $this->opf->addDCMeta(DublinCore::SUBJECT, $this->decodeHtmlEntities($subject)); | ||
1435 | } | ||
1436 | |||
1437 | /** | ||
1438 | * Book source URL, optional. | ||
1439 | * | ||
1440 | * A related resource from which the described resource is derived. | ||
1441 | * | ||
1442 | * The described resource may be derived from the related resource in whole | ||
1443 | * or in part. Recommended best practice is to identify the related | ||
1444 | * resource by means of a string conforming to a formal identification system. | ||
1445 | * | ||
1446 | * Used for the dc:source metadata parameter in the OPF file | ||
1447 | * | ||
1448 | * @param string $sourceURL | ||
1449 | * @access public | ||
1450 | * @return bool $success | ||
1451 | */ | ||
1452 | function setSourceURL($sourceURL) { | ||
1453 | if ($this->isFinalized) { | ||
1454 | return FALSE; | ||
1455 | } | ||
1456 | $this->sourceURL = $sourceURL; | ||
1457 | return TRUE; | ||
1458 | } | ||
1459 | |||
1460 | /** | ||
1461 | * Get Book sourceURL. | ||
1462 | * | ||
1463 | * @access public | ||
1464 | * @return $sourceURL | ||
1465 | */ | ||
1466 | function getSourceURL() { | ||
1467 | return $this->sourceURL; | ||
1468 | } | ||
1469 | |||
1470 | /** | ||
1471 | * Coverage, optional. | ||
1472 | * | ||
1473 | * The spatial or temporal topic of the resource, the spatial applicability | ||
1474 | * of the resource, or the jurisdiction under which the resource is relevant. | ||
1475 | * | ||
1476 | * Spatial topic and spatial applicability may be a named place or a location | ||
1477 | * specified by its geographic coordinates. Temporal topic may be a named | ||
1478 | * period, date, or date range. A jurisdiction may be a named administrative | ||
1479 | * entity or a geographic place to which the resource applies. Recommended | ||
1480 | * best practice is to use a controlled vocabulary such as the Thesaurus of | ||
1481 | * Geographic Names [TGN]. Where appropriate, named places or time periods | ||
1482 | * can be used in preference to numeric identifiers such as sets of | ||
1483 | * coordinates or date ranges. | ||
1484 | * | ||
1485 | * Used for the dc:coverage metadata parameter in the OPF file | ||
1486 | * | ||
1487 | * Same as ->addDublinCoreMetadata(DublinCore::COVERAGE, $coverage); | ||
1488 | * | ||
1489 | * @param string $coverage | ||
1490 | * @access public | ||
1491 | * @return bool $success | ||
1492 | */ | ||
1493 | function setCoverage($coverage) { | ||
1494 | if ($this->isFinalized) { | ||
1495 | return FALSE; | ||
1496 | } | ||
1497 | $this->coverage = $coverage; | ||
1498 | return TRUE; | ||
1499 | } | ||
1500 | |||
1501 | /** | ||
1502 | * Get Book coverage. | ||
1503 | * | ||
1504 | * @access public | ||
1505 | * @return $coverage | ||
1506 | */ | ||
1507 | function getCoverage() { | ||
1508 | return $this->coverage; | ||
1509 | } | ||
1510 | |||
1511 | /** | ||
1512 | * Set book Relation. | ||
1513 | * | ||
1514 | * A related resource. | ||
1515 | * | ||
1516 | * Recommended best practice is to identify the related resource by means | ||
1517 | * of a string conforming to a formal identification system. | ||
1518 | * | ||
1519 | * @param string $relation | ||
1520 | */ | ||
1521 | function setRelation($relation) { | ||
1522 | if ($this->isFinalized) { | ||
1523 | return; | ||
1524 | } | ||
1525 | $this->relation = $relation; | ||
1526 | } | ||
1527 | |||
1528 | /** | ||
1529 | * Get the book relation. | ||
1530 | * | ||
1531 | * @return string The relation. | ||
1532 | */ | ||
1533 | function getRelation() { | ||
1534 | return $this->relation; | ||
1535 | } | ||
1536 | |||
1537 | /** | ||
1538 | * Set book Generator. | ||
1539 | * | ||
1540 | * The generator is a meta tag added to the ncx file, it is not visible | ||
1541 | * from within the book, but is a kind of electronic watermark. | ||
1542 | * | ||
1543 | * @param string $generator | ||
1544 | */ | ||
1545 | function setGenerator($generator) { | ||
1546 | if ($this->isFinalized) { | ||
1547 | return; | ||
1548 | } | ||
1549 | $this->generator = $generator; | ||
1550 | } | ||
1551 | |||
1552 | /** | ||
1553 | * Get the book relation. | ||
1554 | * | ||
1555 | * @return string The generator identity string. | ||
1556 | */ | ||
1557 | function getGenerator() { | ||
1558 | return $this->generator; | ||
1559 | } | ||
1560 | |||
1561 | /** | ||
1562 | * Set ePub date formate to the short yyyy-mm-dd form, for compliance with | ||
1563 | * a bug in EpubCheck, prior to its version 1.1. | ||
1564 | * | ||
1565 | * The latest version of ePubCheck can be obtained here: | ||
1566 | * http://code.google.com/p/epubcheck/ | ||
1567 | * | ||
1568 | * @access public | ||
1569 | * @return bool $success | ||
1570 | */ | ||
1571 | function setShortDateFormat() { | ||
1572 | if ($this->isFinalized) { | ||
1573 | return FALSE; | ||
1574 | } | ||
1575 | $this->dateformat = $this->dateformatShort; | ||
1576 | return TRUE; | ||
1577 | } | ||
1578 | |||
1579 | /** | ||
1580 | * @Deprecated | ||
1581 | */ | ||
1582 | function setIgnoreEmptyBuffer($ignoreEmptyBuffer = TRUE) { | ||
1583 | die ("Function was deprecated, functionality is no longer needed."); | ||
1584 | } | ||
1585 | |||
1586 | /** | ||
1587 | * Set the references title for the ePub 3 landmarks section | ||
1588 | * | ||
1589 | * @param string $referencesTitle | ||
1590 | * @param string $referencesId | ||
1591 | * @param string $referencesClass | ||
1592 | * @return bool | ||
1593 | */ | ||
1594 | function setReferencesTitle($referencesTitle = "Guide", $referencesId = "", $referencesClass = "references") { | ||
1595 | if ($this->isFinalized) { | ||
1596 | return FALSE; | ||
1597 | } | ||
1598 | $this->ncx->referencesTitle = is_string($referencesTitle) ? trim($referencesTitle) : "Guide"; | ||
1599 | $this->ncx->referencesId = is_string($referencesId) ? trim($referencesId) : "references"; | ||
1600 | $this->ncx->referencesClass = is_string($referencesClass) ? trim($referencesClass) : "references"; | ||
1601 | return TRUE; | ||
1602 | } | ||
1603 | |||
1604 | /** | ||
1605 | * Set the references title for the ePub 3 landmarks section | ||
1606 | * | ||
1607 | * @param bool $referencesTitle | ||
1608 | */ | ||
1609 | function setisReferencesAddedToToc($isReferencesAddedToToc = TRUE) { | ||
1610 | if ($this->isFinalized) { | ||
1611 | return FALSE; | ||
1612 | } | ||
1613 | $this->isReferencesAddedToToc = $isReferencesAddedToToc === TRUE; | ||
1614 | return TRUE; | ||
1615 | } | ||
1616 | |||
1617 | /** | ||
1618 | * Get Book status. | ||
1619 | * | ||
1620 | * @access public | ||
1621 | * @return bool | ||
1622 | */ | ||
1623 | function isFinalized() { | ||
1624 | return $this->isFinalized; | ||
1625 | } | ||
1626 | |||
1627 | /** | ||
1628 | * Build the Table of Contents. This is not strictly necessary, as most eReaders will build it from the navigation structure in the .ncx file. | ||
1629 | * | ||
1630 | * @param string $cssFileName Include a link to this css file in the TOC html. | ||
1631 | * @param string $tocCSSClass The TOC is a <div>, if you need special formatting, you can add a css class for that div. Default is "toc". | ||
1632 | * @param string $title Title of the Table of contents. Default is "Table of Contents". Use this for ie. languages other than English. | ||
1633 | * @param bool $addReferences include reference pages in the TOC, using the $referencesOrder array to determine the order of the pages in the TOC. Default is TRUE. | ||
1634 | * @param bool $addToIndex Add the TOC to the NCX index at the current leve/position. Default is FALSE | ||
1635 | * @param string $tocFileName Change teh default name of the TOC file. The default is "TOC.xhtml" | ||
1636 | */ | ||
1637 | function buildTOC($cssFileName = NULL, $tocCSSClass = "toc", $title = "Table of Contents", $addReferences = TRUE, $addToIndex = FALSE, $tocFileName = "TOC.xhtml") { | ||
1638 | if ($this->isFinalized) { | ||
1639 | return FALSE; | ||
1640 | } | ||
1641 | $this->buildTOC = TRUE; | ||
1642 | $this->tocTitle = $title; | ||
1643 | $this->tocFileName = $this->normalizeFileName($tocFileName); | ||
1644 | if (!empty($cssFileName)) { | ||
1645 | $this->tocCSSFileName = $this->normalizeFileName($cssFileName); | ||
1646 | } | ||
1647 | $this->tocCSSClass = $tocCSSClass; | ||
1648 | $this->tocAddReferences = $addReferences; | ||
1649 | |||
1650 | $this->opf->addItemRef("ref_" . Reference::TABLE_OF_CONTENTS, FALSE); | ||
1651 | $this->opf->addReference(Reference::TABLE_OF_CONTENTS, $title, $this->tocFileName); | ||
1652 | |||
1653 | if ($addToIndex) { | ||
1654 | $navPoint = new NavPoint($this->decodeHtmlEntities($title), $this->tocFileName, "ref_" . Reference::TABLE_OF_CONTENTS); | ||
1655 | $this->ncx->addNavPoint($navPoint); | ||
1656 | } else { | ||
1657 | $this->ncx->referencesList[Reference::TABLE_OF_CONTENTS] = $this->tocFileName; | ||
1658 | $this->ncx->referencesName[Reference::TABLE_OF_CONTENTS] = $title; | ||
1659 | } | ||
1660 | } | ||
1661 | |||
1662 | private function finalizeTOC() { | ||
1663 | if (!$this->buildTOC) { | ||
1664 | return FALSE; | ||
1665 | } | ||
1666 | |||
1667 | if (empty($this->tocTitle)) { | ||
1668 | $this->tocTitle = "Table of Contents"; | ||
1669 | } | ||
1670 | |||
1671 | $tocData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; | ||
1672 | |||
1673 | if ($this->isEPubVersion2()) { | ||
1674 | $tocData .= "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n" | ||
1675 | . " \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n" | ||
1676 | . "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" | ||
1677 | . "<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n"; | ||
1678 | } else { | ||
1679 | $tocData .= "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n" | ||
1680 | . "<head>\n<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n"; | ||
1681 | } | ||
1682 | |||
1683 | if (!empty($this->tocCssFileName)) { | ||
1684 | $tocData .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"" . $this->tocCssFileName . "\" />\n"; | ||
1685 | } | ||
1686 | |||
1687 | $tocData .= "<title>" . $this->tocTitle . "</title>\n" | ||
1688 | . "</head>\n" | ||
1689 | . "<body>\n" | ||
1690 | . "<h3>" . $this->tocTitle . "</h3>\n<div"; | ||
1691 | |||
1692 | if (!empty($this->tocCSSClass)) { | ||
1693 | $tocData .= " class=\"" . $this->tocCSSClass . "\""; | ||
1694 | } | ||
1695 | $tocData .= ">\n"; | ||
1696 | |||
1697 | while (list($item, $descriptive) = each($this->referencesOrder)) { | ||
1698 | if ($item === "text") { | ||
1699 | while (list($chapterName, $navPoint) = each($this->ncx->chapterList)) { | ||
1700 | $fileName = $navPoint->getContentSrc(); | ||
1701 | $level = $navPoint->getLevel() -2; | ||
1702 | $tocData .= "\t<p>" . str_repeat("      ", $level) . "<a href=\"" . $fileName . "\">" . $chapterName . "</a></p>\n"; | ||
1703 | } | ||
1704 | } else if ($this->tocAddReferences === TRUE) { | ||
1705 | if (array_key_exists($item, $this->ncx->referencesList)) { | ||
1706 | $tocData .= "\t<p><a href=\"" . $this->ncx->referencesList[$item] . "\">" . $descriptive . "</a></p>\n"; | ||
1707 | } else if ($item === "toc") { | ||
1708 | $tocData .= "\t<p><a href=\"TOC.xhtml\">" . $this->tocTitle . "</a></p>\n"; | ||
1709 | } else if ($item === "cover" && $this->isCoverImageSet) { | ||
1710 | $tocData .= "\t<p><a href=\"CoverPage.xhtml\">" . $descriptive . "</a></p>\n"; | ||
1711 | } | ||
1712 | } | ||
1713 | } | ||
1714 | $tocData .= "</div>\n</body>\n</html>\n"; | ||
1715 | |||
1716 | $this->addReferencePage($this->tocTitle, $this->tocFileName, $tocData, Reference::TABLE_OF_CONTENTS); | ||
1717 | |||
1718 | } | ||
1719 | |||
1720 | /** | ||
1721 | * @return bool | ||
1722 | */ | ||
1723 | function isEPubVersion2() { | ||
1724 | return $this->bookVersion === EPub::BOOK_VERSION_EPUB2; | ||
1725 | } | ||
1726 | |||
1727 | /** | ||
1728 | * @param string $cssFileName | ||
1729 | * @param string $title | ||
1730 | * @return string | ||
1731 | */ | ||
1732 | function buildEPub3TOC($cssFileName = NULL, $title = "Table of Contents") { | ||
1733 | $this->ncx->referencesOrder = $this->referencesOrder; | ||
1734 | $this->ncx->setDocTitle($this->decodeHtmlEntities($this->title)); | ||
1735 | return $this->ncx->finalizeEPub3($title, $cssFileName); | ||
1736 | } | ||
1737 | |||
1738 | /** | ||
1739 | * @param string $fileName | ||
1740 | * @param string $tocData | ||
1741 | * @return bool | ||
1742 | */ | ||
1743 | function addEPub3TOC($fileName, $tocData) { | ||
1744 | if ($this->isEPubVersion2() || $this->isFinalized || array_key_exists($fileName, $this->fileList)) { | ||
1745 | return FALSE; | ||
1746 | } | ||
1747 | $fileName = Zip::getRelativePath($fileName); | ||
1748 | $fileName = preg_replace('#^[/\.]+#i', "", $fileName); | ||
1749 | |||
1750 | $this->zip->addFile($tocData, $this->bookRoot.$fileName); | ||
1751 | |||
1752 | $this->fileList[$fileName] = $fileName; | ||
1753 | $this->opf->addItem("toc", $fileName, "application/xhtml+xml", "nav"); | ||
1754 | return TRUE; | ||
1755 | } | ||
1756 | |||
1757 | /** | ||
1758 | * Check for mandatory parameters and finalize the e-book. | ||
1759 | * Once finalized, the book is locked for further additions. | ||
1760 | * | ||
1761 | * @return bool $success | ||
1762 | */ | ||
1763 | function finalize() { | ||
1764 | if ($this->isFinalized || $this->chapterCount == 0 || empty($this->title) || empty($this->language)) { | ||
1765 | return FALSE; | ||
1766 | } | ||
1767 | |||
1768 | if (empty($this->identifier) || empty($this->identifierType)) { | ||
1769 | $this->setIdentifier($this->createUUID(4), EPub::IDENTIFIER_UUID); | ||
1770 | } | ||
1771 | |||
1772 | if ($this->date == 0) { | ||
1773 | $this->date = time(); | ||
1774 | } | ||
1775 | |||
1776 | if (empty($this->sourceURL)) { | ||
1777 | $this->sourceURL = $this->getCurrentPageURL(); | ||
1778 | } | ||
1779 | |||
1780 | if (empty($this->publisherURL)) { | ||
1781 | $this->sourceURL = $this->getCurrentServerURL(); | ||
1782 | } | ||
1783 | |||
1784 | // Generate OPF data: | ||
1785 | $this->opf->setIdent("BookId"); | ||
1786 | $this->opf->initialize($this->title, $this->language, $this->identifier, $this->identifierType); | ||
1787 | |||
1788 | $DCdate = new DublinCore(DublinCore::DATE, gmdate($this->dateformat, $this->date)); | ||
1789 | $DCdate->addOpfAttr("event", "publication"); | ||
1790 | $this->opf->metadata->addDublinCore($DCdate); | ||
1791 | |||
1792 | if (!empty($this->description)) { | ||
1793 | $this->opf->addDCMeta(DublinCore::DESCRIPTION, $this->decodeHtmlEntities($this->description)); | ||
1794 | } | ||
1795 | |||
1796 | if (!empty($this->publisherName)) { | ||
1797 | $this->opf->addDCMeta(DublinCore::PUBLISHER, $this->decodeHtmlEntities($this->publisherName)); | ||
1798 | } | ||
1799 | |||
1800 | if (!empty($this->publisherURL)) { | ||
1801 | $this->opf->addDCMeta(DublinCore::RELATION, $this->decodeHtmlEntities($this->publisherURL)); | ||
1802 | } | ||
1803 | |||
1804 | if (!empty($this->author)) { | ||
1805 | $author = $this->decodeHtmlEntities($this->author); | ||
1806 | $this->opf->addCreator($author, $this->decodeHtmlEntities($this->authorSortKey), MarcCode::AUTHOR); | ||
1807 | $this->ncx->setDocAuthor($author); | ||
1808 | } | ||
1809 | |||
1810 | if (!empty($this->rights)) { | ||
1811 | $this->opf->addDCMeta(DublinCore::RIGHTS, $this->decodeHtmlEntities($this->rights)); | ||
1812 | } | ||
1813 | |||
1814 | if (!empty($this->coverage)) { | ||
1815 | $this->opf->addDCMeta(DublinCore::COVERAGE, $this->decodeHtmlEntities($this->coverage)); | ||
1816 | } | ||
1817 | |||
1818 | if (!empty($this->sourceURL)) { | ||
1819 | $this->opf->addDCMeta(DublinCore::SOURCE, $this->sourceURL); | ||
1820 | } | ||
1821 | |||
1822 | if (!empty($this->relation)) { | ||
1823 | $this->opf->addDCMeta(DublinCore::RELATION, $this->decodeHtmlEntities($this->relation)); | ||
1824 | } | ||
1825 | |||
1826 | if ($this->isCoverImageSet) { | ||
1827 | $this->opf->addMeta("cover", "coverImage"); | ||
1828 | } | ||
1829 | |||
1830 | if (!empty($this->generator)) { | ||
1831 | $gen = $this->decodeHtmlEntities($this->generator); | ||
1832 | $this->opf->addMeta("generator", $gen); | ||
1833 | $this->ncx->addMetaEntry("dtb:generator", $gen); | ||
1834 | } | ||
1835 | |||
1836 | if ($this->EPubMark) { | ||
1837 | $this->opf->addMeta("generator", "EPub (Version " . self::VERSION . ") by A. Grandt, http://www.phpclasses.org/package/6115"); | ||
1838 | } | ||
1839 | |||
1840 | reset($this->ncx->chapterList); | ||
1841 | list($firstChapterName, $firstChapterNavPoint) = each($this->ncx->chapterList); | ||
1842 | $firstChapterFileName = $firstChapterNavPoint->getContentSrc(); | ||
1843 | $this->opf->addReference(Reference::TEXT, $this->decodeHtmlEntities($firstChapterName), $firstChapterFileName); | ||
1844 | |||
1845 | $this->ncx->setUid($this->identifier); | ||
1846 | |||
1847 | $this->ncx->setDocTitle($this->decodeHtmlEntities($this->title)); | ||
1848 | |||
1849 | $this->ncx->referencesOrder = $this->referencesOrder; | ||
1850 | if ($this->isReferencesAddedToToc) { | ||
1851 | $this->ncx->finalizeReferences(); | ||
1852 | } | ||
1853 | |||
1854 | $this->finalizeTOC(); | ||
1855 | |||
1856 | if (!$this->isEPubVersion2()) { | ||
1857 | $this->addEPub3TOC("epub3toc.xhtml", $this->buildEPub3TOC()); | ||
1858 | } | ||
1859 | |||
1860 | $opfFinal = $this->fixEncoding($this->opf->finalize()); | ||
1861 | $ncxFinal = $this->fixEncoding($this->ncx->finalize()); | ||
1862 | |||
1863 | if (mb_detect_encoding($opfFinal, 'UTF-8', true) === "UTF-8") { | ||
1864 | $this->zip->addFile($opfFinal, $this->bookRoot."book.opf"); | ||
1865 | } else { | ||
1866 | $this->zip->addFile(mb_convert_encoding($opfFinal, "UTF-8"), $this->bookRoot."book.opf"); | ||
1867 | } | ||
1868 | |||
1869 | if (mb_detect_encoding($ncxFinal, 'UTF-8', true) === "UTF-8") { | ||
1870 | $this->zip->addFile($ncxFinal, $this->bookRoot."book.ncx"); | ||
1871 | } else { | ||
1872 | $this->zip->addFile(mb_convert_encoding($ncxFinal, "UTF-8"), $this->bookRoot."book.ncx"); | ||
1873 | } | ||
1874 | |||
1875 | $this->opf = NULL; | ||
1876 | $this->ncx = NULL; | ||
1877 | |||
1878 | $this->isFinalized = TRUE; | ||
1879 | return TRUE; | ||
1880 | } | ||
1881 | |||
1882 | /** | ||
1883 | * Ensure the encoded string is a valid UTF-8 string. | ||
1884 | * | ||
1885 | * Note, that a mb_detect_encoding on the returned string will still return ASCII if the entire string is comprized of characters in the 1-127 range. | ||
1886 | * | ||
1887 | * @link: http://snippetdb.com/php/convert-string-to-utf-8-for-mysql | ||
1888 | * @param string $in_str | ||
1889 | * @return string converted string. | ||
1890 | */ | ||
1891 | function fixEncoding($in_str) { | ||
1892 | if (mb_detect_encoding($in_str) == "UTF-8" && mb_check_encoding($in_str,"UTF-8")) { | ||
1893 | return $in_str; | ||
1894 | } else { | ||
1895 | return utf8_encode($in_str); | ||
1896 | } | ||
1897 | } | ||
1898 | |||
1899 | /** | ||
1900 | * Return the finalized book. | ||
1901 | * | ||
1902 | * @return string with the book in binary form. | ||
1903 | */ | ||
1904 | function getBook() { | ||
1905 | if (!$this->isFinalized) { | ||
1906 | $this->finalize(); | ||
1907 | } | ||
1908 | |||
1909 | return $this->zip->getZipData(); | ||
1910 | } | ||
1911 | |||
1912 | /** | ||
1913 | * Remove disallowed characters from string to get a nearly safe filename | ||
1914 | * | ||
1915 | * @param string $fileName | ||
1916 | * @return mixed|string | ||
1917 | */ | ||
1918 | function sanitizeFileName($fileName) { | ||
1919 | $fileName1 = str_replace($this->forbiddenCharacters, '', $fileName); | ||
1920 | $fileName2 = preg_replace('/[\s-]+/', '-', $fileName1); | ||
1921 | return trim($fileName2, '.-_'); | ||
1922 | |||
1923 | } | ||
1924 | |||
1925 | /** | ||
1926 | * Cleanup the filepath, and remove leading . and / characters. | ||
1927 | * | ||
1928 | * Sometimes, when a path is generated from multiple fragments, | ||
1929 | * you can get something like "../data/html/../images/image.jpeg" | ||
1930 | * ePub files don't work well with that, this will normalize that | ||
1931 | * example path to "data/images/image.jpeg" | ||
1932 | * | ||
1933 | * @param string $fileName | ||
1934 | * @return string normalized filename | ||
1935 | */ | ||
1936 | function normalizeFileName($fileName) { | ||
1937 | return preg_replace('#^[/\.]+#i', "", Zip::getRelativePath($fileName)); | ||
1938 | } | ||
1939 | |||
1940 | /** | ||
1941 | * Save the ePub file to local disk. | ||
1942 | * | ||
1943 | * @param string $fileName | ||
1944 | * @param string $baseDir If empty baseDir is absolute to server path, if omitted it's relative to script path | ||
1945 | * @return The sent file name if successfull, FALSE if it failed. | ||
1946 | */ | ||
1947 | function saveBook($fileName, $baseDir = '.') { | ||
1948 | |||
1949 | // Make fileName safe | ||
1950 | $fileName = $this->sanitizeFileName($fileName); | ||
1951 | |||
1952 | // Finalize book, if it's not done already | ||
1953 | if (!$this->isFinalized) { | ||
1954 | $this->finalize(); | ||
1955 | } | ||
1956 | |||
1957 | if (stripos(strrev($fileName), "bupe.") !== 0) { | ||
1958 | $fileName .= ".epub"; | ||
1959 | } | ||
1960 | |||
1961 | // Try to open file access | ||
1962 | $fh = fopen($baseDir.'/'.$fileName, "w"); | ||
1963 | |||
1964 | if ($fh) { | ||
1965 | fputs($fh, $this->getBook()); | ||
1966 | fclose($fh); | ||
1967 | |||
1968 | // if file is written return TRUE | ||
1969 | return $fileName; | ||
1970 | } | ||
1971 | |||
1972 | // return FALSE by default | ||
1973 | return FALSE; | ||
1974 | } | ||
1975 | |||
1976 | /** | ||
1977 | * Return the finalized book size. | ||
1978 | * | ||
1979 | * @return string | ||
1980 | */ | ||
1981 | function getBookSize() { | ||
1982 | if (!$this->isFinalized) { | ||
1983 | $this->finalize(); | ||
1984 | } | ||
1985 | |||
1986 | return $this->zip->getArchiveSize(); | ||
1987 | } | ||
1988 | |||
1989 | /** | ||
1990 | * Send the book as a zip download | ||
1991 | * | ||
1992 | * Sending will fail if the output buffer is in use. You can override this limit by | ||
1993 | * calling setIgnoreEmptyBuffer(TRUE), though the function will still fail if that | ||
1994 | * buffer is not empty. | ||
1995 | * | ||
1996 | * @param string $fileName The name of the book without the .epub at the end. | ||
1997 | * @return The sent file name if successfull, FALSE if it failed. | ||
1998 | */ | ||
1999 | function sendBook($fileName) { | ||
2000 | if (!$this->isFinalized) { | ||
2001 | $this->finalize(); | ||
2002 | } | ||
2003 | |||
2004 | if (stripos(strrev($fileName), "bupe.") !== 0) { | ||
2005 | $fileName .= ".epub"; | ||
2006 | } | ||
2007 | |||
2008 | if (TRUE === $this->zip->sendZip($fileName, "application/epub+zip")) { | ||
2009 | return $fileName; | ||
2010 | } | ||
2011 | return FALSE; | ||
2012 | } | ||
2013 | |||
2014 | /** | ||
2015 | * Generates an UUID. | ||
2016 | * | ||
2017 | * Default version (4) will generate a random UUID, version 3 will URL based UUID. | ||
2018 | * | ||
2019 | * Added for convinience | ||
2020 | * | ||
2021 | * @param int $bookVersion UUID version to retrieve, See lib.uuid.manual.html for details. | ||
2022 | * @param string $url | ||
2023 | * @return string The formatted uuid | ||
2024 | */ | ||
2025 | function createUUID($bookVersion = 4, $url = NULL) { | ||
2026 | include_once("lib.uuid.php"); | ||
2027 | return UUID::mint($bookVersion, $url, UUID::nsURL); | ||
2028 | } | ||
2029 | |||
2030 | /** | ||
2031 | * Get the url of the current page. | ||
2032 | * Example use: Default Source URL | ||
2033 | * | ||
2034 | * $return string Page URL. | ||
2035 | */ | ||
2036 | function getCurrentPageURL() { | ||
2037 | $pageURL = $this->getCurrentServerURL() . filter_input(INPUT_SERVER, "REQUEST_URI"); | ||
2038 | return $pageURL; | ||
2039 | } | ||
2040 | |||
2041 | /** | ||
2042 | * Get the url of the server. | ||
2043 | * Example use: Default Publisher URL | ||
2044 | * | ||
2045 | * $return string Server URL. | ||
2046 | */ | ||
2047 | function getCurrentServerURL() { | ||
2048 | $serverURL = 'http'; | ||
2049 | $https = filter_input(INPUT_SERVER, "HTTPS"); | ||
2050 | $port = filter_input(INPUT_SERVER, "SERVER_PORT"); | ||
2051 | |||
2052 | if ($https === "on") { | ||
2053 | $serverURL .= "s"; | ||
2054 | } | ||
2055 | $serverURL .= "://" . filter_input(INPUT_SERVER, "SERVER_NAME"); | ||
2056 | if ($port != "80") { | ||
2057 | $serverURL .= ":" . $port; | ||
2058 | } | ||
2059 | return $serverURL . '/'; | ||
2060 | } | ||
2061 | |||
2062 | /** | ||
2063 | * Try to determine the mimetype of the file path. | ||
2064 | * | ||
2065 | * @param string $source Path | ||
2066 | * @return string mimetype, or FALSE. | ||
2067 | */ | ||
2068 | function getMime($source) { | ||
2069 | return $this->mimetypes[pathinfo($source, PATHINFO_EXTENSION)]; | ||
2070 | } | ||
2071 | |||
2072 | /** | ||
2073 | * Get an image from a file or url, return it resized if the image exceeds the $maxImageWidth or $maxImageHeight directives. | ||
2074 | * | ||
2075 | * The return value is an array. | ||
2076 | * ['width'] is the width of the image. | ||
2077 | * ['height'] is the height of the image. | ||
2078 | * ['mime'] is the mime type of the image. Resized images are always in jpeg format. | ||
2079 | * ['image'] is the image data. | ||
2080 | * ['ext'] is the extension of the image file. | ||
2081 | * | ||
2082 | * @param string $source path or url to file. | ||
2083 | * $return array | ||
2084 | */ | ||
2085 | function getImage($source) { | ||
2086 | $width = -1; | ||
2087 | $height = -1; | ||
2088 | $mime = "application/octet-stream"; | ||
2089 | $type = FALSE; | ||
2090 | $ext = ""; | ||
2091 | |||
2092 | |||
2093 | $image = $this->getFileContents($source); | ||
2094 | |||
2095 | if ($image !== FALSE && strlen($image) > 0) { | ||
2096 | $imageFile = imagecreatefromstring($image); | ||
2097 | if ($imageFile !== false) { | ||
2098 | $width = ImageSX($imageFile); | ||
2099 | $height = ImageSY($imageFile); | ||
2100 | } | ||
2101 | if ($this->isExifInstalled) { | ||
2102 | @$type = exif_imagetype($source); | ||
2103 | $mime = image_type_to_mime_type($type); | ||
2104 | } | ||
2105 | if ($mime === "application/octet-stream") { | ||
2106 | $mime = $this->image_file_type_from_binary($image); | ||
2107 | } | ||
2108 | if ($mime === "application/octet-stream") { | ||
2109 | $mime = $this->getMimeTypeFromUrl($source); | ||
2110 | } | ||
2111 | } else { | ||
2112 | return FALSE; | ||
2113 | } | ||
2114 | |||
2115 | if ($width <= 0 || $height <= 0) { | ||
2116 | return FALSE; | ||
2117 | } | ||
2118 | |||
2119 | $ratio = 1; | ||
2120 | |||
2121 | if ($this->isGdInstalled) { | ||
2122 | if ($width > $this->maxImageWidth) { | ||
2123 | $ratio = $this->maxImageWidth/$width; | ||
2124 | } | ||
2125 | if ($height*$ratio > $this->maxImageHeight) { | ||
2126 | $ratio = $this->maxImageHeight/$height; | ||
2127 | } | ||
2128 | |||
2129 | if ($ratio < 1 || empty($mime) || ($this->isGifImagesEnabled !== FALSE && $mime == "image/gif")) { | ||
2130 | $image_o = imagecreatefromstring($image); | ||
2131 | $image_p = imagecreatetruecolor($width*$ratio, $height*$ratio); | ||
2132 | |||
2133 | if ($mime == "image/png") { | ||
2134 | imagealphablending($image_p, false); | ||
2135 | imagesavealpha($image_p, true); | ||
2136 | imagealphablending($image_o, true); | ||
2137 | |||
2138 | imagecopyresampled($image_p, $image_o, 0, 0, 0, 0, ($width*$ratio), ($height*$ratio), $width, $height); | ||
2139 | ob_start(); | ||
2140 | imagepng($image_p, NULL, 9); | ||
2141 | $image = ob_get_contents(); | ||
2142 | ob_end_clean(); | ||
2143 | |||
2144 | $ext = "png"; | ||
2145 | } else { | ||
2146 | imagecopyresampled($image_p, $image_o, 0, 0, 0, 0, ($width*$ratio), ($height*$ratio), $width, $height); | ||
2147 | ob_start(); | ||
2148 | imagejpeg($image_p, NULL, 80); | ||
2149 | $image = ob_get_contents(); | ||
2150 | ob_end_clean(); | ||
2151 | |||
2152 | $mime = "image/jpeg"; | ||
2153 | $ext = "jpg"; | ||
2154 | } | ||
2155 | imagedestroy($image_o); | ||
2156 | imagedestroy($image_p); | ||
2157 | } | ||
2158 | } | ||
2159 | |||
2160 | if ($ext === "") { | ||
2161 | static $mimeToExt = array ( | ||
2162 | 'image/jpeg' => 'jpg', | ||
2163 | 'image/gif' => 'gif', | ||
2164 | 'image/png' => 'png' | ||
2165 | ); | ||
2166 | |||
2167 | if (isset($mimeToExt[$mime])) { | ||
2168 | $ext = $mimeToExt[$mime]; | ||
2169 | } | ||
2170 | } | ||
2171 | |||
2172 | $rv = array(); | ||
2173 | $rv['width'] = $width*$ratio; | ||
2174 | $rv['height'] = $height*$ratio; | ||
2175 | $rv['mime'] = $mime; | ||
2176 | $rv['image'] = $image; | ||
2177 | $rv['ext'] = $ext; | ||
2178 | |||
2179 | return $rv; | ||
2180 | } | ||
2181 | |||
2182 | /** | ||
2183 | * Get file contents, using curl if available, else file_get_contents | ||
2184 | * | ||
2185 | * @param string $source | ||
2186 | * @return bool | ||
2187 | */ | ||
2188 | function getFileContents($source, $toTempFile = FALSE) { | ||
2189 | $isExternal = preg_match('#^(http|ftp)s?://#i', $source) == 1; | ||
2190 | |||
2191 | if ($isExternal && $this->isCurlInstalled) { | ||
2192 | $ch = curl_init(); | ||
2193 | $outFile = NULL; | ||
2194 | $fp = NULL; | ||
2195 | $res = FALSE; | ||
2196 | $info = array('http_code' => 500); | ||
2197 | |||
2198 | curl_setopt($ch, CURLOPT_HEADER, 0); | ||
2199 | curl_setopt($ch, CURLOPT_URL, str_replace(" ","%20",$source)); | ||
2200 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | ||
2201 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); | ||
2202 | curl_setopt($ch, CURLOPT_BUFFERSIZE, 4096); | ||
2203 | |||
2204 | if ($toTempFile) { | ||
2205 | $outFile = tempnam(sys_get_temp_dir(), "EPub_v" . EPub::VERSION . "_"); | ||
2206 | $fp = fopen($outFile, "w+b"); | ||
2207 | curl_setopt($ch, CURLOPT_FILE, $fp); | ||
2208 | |||
2209 | $res = curl_exec($ch); | ||
2210 | $info = curl_getinfo($ch); | ||
2211 | |||
2212 | curl_close($ch); | ||
2213 | fclose($fp); | ||
2214 | } else { | ||
2215 | $res = curl_exec($ch); | ||
2216 | $info = curl_getinfo($ch); | ||
2217 | |||
2218 | curl_close($ch); | ||
2219 | } | ||
2220 | |||
2221 | if ($info['http_code'] == 200 && $res != false) { | ||
2222 | if ($toTempFile) { | ||
2223 | return $outFile; | ||
2224 | } | ||
2225 | return $res; | ||
2226 | } | ||
2227 | return FALSE; | ||
2228 | } | ||
2229 | |||
2230 | if ($this->isFileGetContentsInstalled && (!$isExternal || $this->isFileGetContentsExtInstalled)) { | ||
2231 | @$data = file_get_contents($source); | ||
2232 | return $data; | ||
2233 | } | ||
2234 | return FALSE; | ||
2235 | } | ||
2236 | |||
2237 | /** | ||
2238 | * get mime type from image data | ||
2239 | * | ||
2240 | * By fireweasel found on http://stackoverflow.com/questions/2207095/get-image-mimetype-from-resource-in-php-gd | ||
2241 | * @staticvar array $type | ||
2242 | * @param object $binary | ||
2243 | * @return string | ||
2244 | */ | ||
2245 | function image_file_type_from_binary($binary) { | ||
2246 | $hits = 0; | ||
2247 | if (!preg_match( | ||
2248 | '/\A(?:(\xff\xd8\xff)|(GIF8[79]a)|(\x89PNG\x0d\x0a)|(BM)|(\x49\x49(?:\x2a\x00|\x00\x4a))|(FORM.{4}ILBM))/', | ||
2249 | $binary, $hits)) { | ||
2250 | return 'application/octet-stream'; | ||
2251 | } | ||
2252 | static $type = array ( | ||
2253 | 1 => 'image/jpeg', | ||
2254 | 2 => 'image/gif', | ||
2255 | 3 => 'image/png', | ||
2256 | 4 => 'image/x-windows-bmp', | ||
2257 | 5 => 'image/tiff', | ||
2258 | 6 => 'image/x-ilbm', | ||
2259 | ); | ||
2260 | return $type[count($hits) - 1]; | ||
2261 | } | ||
2262 | |||
2263 | /** | ||
2264 | * @param string $source URL Source | ||
2265 | * @return string MimeType | ||
2266 | */ | ||
2267 | function getMimeTypeFromUrl($source) { | ||
2268 | $ext = FALSE; | ||
2269 | |||
2270 | $srev = strrev($source); | ||
2271 | $pos = strpos($srev, "?"); | ||
2272 | if ($pos !== FALSE) { | ||
2273 | $srev = substr($srev, $pos+1); | ||
2274 | } | ||
2275 | |||
2276 | $pos = strpos($srev, "."); | ||
2277 | if ($pos !== FALSE) { | ||
2278 | $ext = strtolower(strrev(substr($srev, 0, $pos))); | ||
2279 | } | ||
2280 | |||
2281 | if ($ext !== FALSE) { | ||
2282 | return $this->getMimeTypeFromExtension($ext); | ||
2283 | } | ||
2284 | return "application/octet-stream"; | ||
2285 | } | ||
2286 | |||
2287 | /** | ||
2288 | * @param string $ext Extension | ||
2289 | * @return string MimeType | ||
2290 | */ | ||
2291 | function getMimeTypeFromExtension($ext) { | ||
2292 | switch ($ext) { | ||
2293 | case "jpg": | ||
2294 | case "jpe": | ||
2295 | case "jpeg": | ||
2296 | return 'image/jpeg'; | ||
2297 | case "gif": | ||
2298 | return 'image/gif'; | ||
2299 | case "png": | ||
2300 | return 'image/png'; | ||
2301 | case "bmp": | ||
2302 | return 'image/x-windows-bmp'; | ||
2303 | case "tif": | ||
2304 | case "tiff": | ||
2305 | case "cpt": | ||
2306 | return 'image/tiff'; | ||
2307 | case "lbm": | ||
2308 | case "ilbm": | ||
2309 | return 'image/x-ilbm'; | ||
2310 | default: | ||
2311 | return "application/octet-stream"; | ||
2312 | } | ||
2313 | } | ||
2314 | |||
2315 | /** | ||
2316 | * Encode html code to use html entities, safeguarding it from potential character encoding peoblems | ||
2317 | * This function is a bit different from the vanilla htmlentities function in that it does not encode html tags. | ||
2318 | * | ||
2319 | * The regexp is taken from the PHP Manual discussion, it was written by user "busbyjon". | ||
2320 | * http://www.php.net/manual/en/function.htmlentities.php#90111 | ||
2321 | * | ||
2322 | * @param string $string string to encode. | ||
2323 | */ | ||
2324 | public function encodeHtml($string) { | ||
2325 | $string = strtr($string, $this->html_encoding_characters); | ||
2326 | |||
2327 | //return preg_replace("/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,5};)/", "&\\1", $string); | ||
2328 | //return preg_replace("/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,5};)/", "&", $string); | ||
2329 | return $string; | ||
2330 | } | ||
2331 | |||
2332 | /** | ||
2333 | * Helper function to create a DOM fragment with given markup. | ||
2334 | * | ||
2335 | * @author Adam Schmalhofer | ||
2336 | * | ||
2337 | * @param DOMDocument $dom | ||
2338 | * @param string $markup | ||
2339 | * @return DOMNode fragment in a node. | ||
2340 | */ | ||
2341 | protected function createDomFragment($dom, $markup) { | ||
2342 | $node = $dom->createDocumentFragment(); | ||
2343 | $node->appendXML($markup); | ||
2344 | return $node; | ||
2345 | } | ||
2346 | |||
2347 | /** | ||
2348 | * Retrieve an array of file names currently added to the book. | ||
2349 | * $key is the filename used in the book | ||
2350 | * $value is the original filename, will be the same as $key for most entries | ||
2351 | * | ||
2352 | * @return array file list | ||
2353 | */ | ||
2354 | function getFileList() { | ||
2355 | return $this->fileList; | ||
2356 | } | ||
2357 | |||
2358 | /** | ||
2359 | * @deprecated Use Zip::getRelativePath($relPath) instead. | ||
2360 | */ | ||
2361 | function relPath($relPath) { | ||
2362 | die ("Function was deprecated, use Zip::getRelativePath(\$relPath); instead"); | ||
2363 | } | ||
2364 | |||
2365 | /** | ||
2366 | * Set default chapter target size. | ||
2367 | * Default is 250000 bytes, and minimum is 10240 bytes. | ||
2368 | * | ||
2369 | * @param int $size segment size in bytes | ||
2370 | * @return void | ||
2371 | */ | ||
2372 | function setSplitSize($size) { | ||
2373 | $this->splitDefaultSize = (int)$size; | ||
2374 | if ($size < 10240) { | ||
2375 | $this->splitDefaultSize = 10240; // Making the file smaller than 10k is not a good idea. | ||
2376 | } | ||
2377 | } | ||
2378 | |||
2379 | /** | ||
2380 | * Get the chapter target size. | ||
2381 | * | ||
2382 | * @return $size | ||
2383 | */ | ||
2384 | function getSplitSize() { | ||
2385 | return $this->splitDefaultSize; | ||
2386 | } | ||
2387 | |||
2388 | /** | ||
2389 | * Remove all non essential html tags and entities. | ||
2390 | * | ||
2391 | * @global type $htmlEntities | ||
2392 | * @param string $string | ||
2393 | * @return string with the stripped entities. | ||
2394 | */ | ||
2395 | function decodeHtmlEntities($string) { | ||
2396 | global $htmlEntities; | ||
2397 | |||
2398 | $string = preg_replace('~\s*<br\s*/*\s*>\s*~i', "\n", $string); | ||
2399 | $string = preg_replace('~\s*</(p|div)\s*>\s*~i', "\n\n", $string); | ||
2400 | $string = preg_replace('~<[^>]*>~', '', $string); | ||
2401 | |||
2402 | $string = strtr($string, $htmlEntities); | ||
2403 | |||
2404 | $string = str_replace('&', '&', $string); | ||
2405 | $string = str_replace('&amp;', '&', $string); | ||
2406 | $string = preg_replace('~&(#x*[a-fA-F0-9]+;)~', '&\1', $string); | ||
2407 | $string = str_replace('<', '<', $string); | ||
2408 | $string = str_replace('>', '>', $string); | ||
2409 | |||
2410 | return $string; | ||
2411 | } | ||
2412 | |||
2413 | /** | ||
2414 | * Simply remove all HTML tags, brute force and no finesse. | ||
2415 | * | ||
2416 | * @param string $string html | ||
2417 | * @return string | ||
2418 | */ | ||
2419 | function html2text($string) { | ||
2420 | return preg_replace('~<[^>]*>~', '', $string); | ||
2421 | } | ||
2422 | |||
2423 | /** | ||
2424 | * @return string | ||
2425 | */ | ||
2426 | function getLog() { | ||
2427 | return $this->log->getLog(); | ||
2428 | } | ||
2429 | } | ||
diff --git a/inc/3rdparty/libraries/PHPePub/EPubChapterSplitter.php b/inc/3rdparty/libraries/PHPePub/EPubChapterSplitter.php new file mode 100644 index 00000000..1d44f238 --- /dev/null +++ b/inc/3rdparty/libraries/PHPePub/EPubChapterSplitter.php | |||
@@ -0,0 +1,201 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * Split an HTML file into smaller html files, retaining the formatting and structure for the individual parts. | ||
4 | * What this splitter does is using DOM to try and retain any formatting in the file, including rebuilding the DOM tree for subsequent parts. | ||
5 | * Split size is considered max target size. The actual size is the result of an even split across the resulting files. | ||
6 | * | ||
7 | * @author A. Grandt <php@grandt.com> | ||
8 | * @copyright 2009-2014 A. Grandt | ||
9 | * @license GNU LGPL 2.1 | ||
10 | * @link http://www.phpclasses.org/package/6115 | ||
11 | * @link https://github.com/Grandt/PHPePub | ||
12 | * @version 3.20 | ||
13 | */ | ||
14 | class 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 | */ | ||
10 | class 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 Δ Start ; Δ 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 | */ | ||
18 | class 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 | |||
10 | Copyright (c) 2009 J. King | ||
11 | |||
12 | Permission is hereby granted, free of charge, to any person | ||
13 | obtaining a copy of this software and associated documentation | ||
14 | files (the "Software"), to deal in the Software without | ||
15 | restriction, including without limitation the rights to use, | ||
16 | copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
17 | copies of the Software, and to permit persons to whom the | ||
18 | Software is furnished to do so, subject to the following | ||
19 | conditions: | ||
20 | |||
21 | The above copyright notice and this permission notice shall be | ||
22 | included in all copies or substantial portions of the Software. | ||
23 | |||
24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
25 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | ||
26 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
27 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | ||
28 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||
29 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
30 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
31 | OTHER 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 | ||
4 | by J. King (http://jkingweb.ca/) | ||
5 | Licensed under MIT license | ||
6 | |||
7 | See http://jkingweb.ca/code/php/lib.uuid/ | ||
8 | for documentation | ||
9 | |||
10 | Last revised 2010-02-15 | ||
11 | */ | ||
12 | |||
13 | /* | ||
14 | Copyright (c) 2009 J. King | ||
15 | |||
16 | Permission is hereby granted, free of charge, to any person | ||
17 | obtaining a copy of this software and associated documentation | ||
18 | files (the "Software"), to deal in the Software without | ||
19 | restriction, including without limitation the rights to use, | ||
20 | copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
21 | copies of the Software, and to permit persons to whom the | ||
22 | Software is furnished to do so, subject to the following | ||
23 | conditions: | ||
24 | |||
25 | The above copyright notice and this permission notice shall be | ||
26 | included in all copies or substantial portions of the Software. | ||
27 | |||
28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
29 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | ||
30 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
31 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | ||
32 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||
33 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
34 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
35 | OTHER DEALINGS IN THE SOFTWARE. | ||
36 | */ | ||
37 | |||
38 | |||
39 | class 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 | |||
313 | class UUIDException extends Exception { | ||
314 | } | ||