Version Description
- Feature: New action wpo_wcpdf_init_document
- Fix: Use title getters for my-account and backend buttons
- Fix: Legacy Premium Templates reference
- Tweak: Skip documents overview in settings, default to invoice
Download this release
Release Info
Developer | pomegranate |
Plugin | WooCommerce PDF Invoices & Packing Slips |
Version | 2.1.2 |
Comparing to | |
See all releases |
Code changes from version 2.1.1 to 2.1.2
- assets/css/setup-wizard.css +399 -399
- assets/js/confetti.js +347 -347
- assets/js/order-script.js +42 -42
- assets/js/setup-wizard.js +20 -20
- composer.json +5 -5
- composer.lock +203 -203
- includes/class-wcpdf-admin.php +488 -488
- includes/class-wcpdf-frontend.php +7 -1
- includes/class-wcpdf-install.php +358 -358
- includes/class-wcpdf-main.php +502 -502
- includes/class-wcpdf-pdf-maker.php +60 -60
- includes/class-wcpdf-settings-callbacks.php +502 -502
- includes/class-wcpdf-settings-debug.php +195 -195
- includes/class-wcpdf-settings-documents.php +1 -0
- includes/class-wcpdf-settings-general.php +298 -298
- includes/class-wcpdf-settings.php +254 -254
- includes/class-wcpdf-setup-wizard.php +246 -246
- includes/compatibility/class-wcpdf-compatibility-third-party-plugins.php +162 -162
- includes/documents/abstract-wcpdf-order-document-methods.php +1029 -1029
- includes/documents/abstract-wcpdf-order-document.php +677 -676
- includes/documents/class-wcpdf-invoice.php +352 -352
- includes/documents/class-wcpdf-packing-slip.php +149 -149
- includes/documents/class-wcpdf-sequential-number-store.php +167 -167
- includes/views/attachment-settings-hint.php +23 -23
- includes/views/dompdf-status.php +202 -202
- includes/views/setup-wizard/attach-to.php +23 -23
- includes/views/setup-wizard/display-options.php +18 -18
- includes/views/setup-wizard/good-to-go.php +5 -5
- includes/views/setup-wizard/logo.php +8 -8
- includes/views/setup-wizard/paper-format.php +13 -13
- includes/views/setup-wizard/shop-name.php +10 -10
- includes/views/wcpdf-extensions.php +130 -130
- includes/views/wcpdf-settings-page.php +51 -51
- readme.txt +226 -220
- templates/Simple/invoice.php +144 -144
- templates/Simple/packing-slip.php +112 -112
- vendor/autoload.php +7 -7
- vendor/composer/ClassLoader.php +445 -445
- vendor/composer/LICENSE +21 -21
- vendor/composer/autoload_real.php +52 -52
- vendor/composer/installed.json +193 -193
- vendor/dompdf/dompdf/CONTRIBUTING.md +65 -65
- vendor/dompdf/dompdf/README.md +211 -211
- vendor/dompdf/dompdf/composer.json +44 -44
- vendor/dompdf/dompdf/lib/Cpdf.php +5640 -5640
- vendor/dompdf/dompdf/lib/html5lib/Data.php +123 -123
- vendor/dompdf/dompdf/lib/html5lib/Tokenizer.php +0 -26
assets/css/setup-wizard.css
CHANGED
@@ -1,399 +1,399 @@
|
|
1 |
-
.wpo-wizzard {
|
2 |
-
background:#f9f9f9;
|
3 |
-
}
|
4 |
-
|
5 |
-
.wpo-wcpdf-setup {
|
6 |
-
padding:0;
|
7 |
-
font-family: 'Ubuntu', sans-serif;
|
8 |
-
font-size: 14px;
|
9 |
-
line-height: 22px;
|
10 |
-
color:#444;
|
11 |
-
background:none;
|
12 |
-
-webkit-box-shadow: none;
|
13 |
-
box-shadow: none;
|
14 |
-
}
|
15 |
-
|
16 |
-
.wpo-wcpdf-setup h1, .wpo-wcpdf-setup h2 {
|
17 |
-
border:none;
|
18 |
-
color:#444;
|
19 |
-
margin:0;
|
20 |
-
}
|
21 |
-
|
22 |
-
#confetti {
|
23 |
-
width:100%;
|
24 |
-
height:100%;
|
25 |
-
position:absolute;
|
26 |
-
left:0;
|
27 |
-
top:0;
|
28 |
-
z-index:-999;
|
29 |
-
}
|
30 |
-
|
31 |
-
.wpo-setup {
|
32 |
-
background-color:#f9f9f9;
|
33 |
-
font-family: 'Ubuntu', sans-serif;
|
34 |
-
font-size: 14px;
|
35 |
-
line-height: 22px;
|
36 |
-
color:#444;
|
37 |
-
width:100%;
|
38 |
-
height:100%;
|
39 |
-
margin:0;
|
40 |
-
padding:0;
|
41 |
-
}
|
42 |
-
|
43 |
-
.confetti {
|
44 |
-
width:100%;
|
45 |
-
height:100%;
|
46 |
-
z-index:-999;
|
47 |
-
position:relative;
|
48 |
-
}
|
49 |
-
|
50 |
-
.wpo-setup-card {
|
51 |
-
display:block;
|
52 |
-
background-color:#f2fafa;
|
53 |
-
max-width:900px;
|
54 |
-
margin:100px auto 100px auto;
|
55 |
-
padding:0;
|
56 |
-
border-radius:10px;
|
57 |
-
box-shadow:10px 10px 80px rgba(0,0,0,0.05);
|
58 |
-
overflow:hidden;
|
59 |
-
}
|
60 |
-
|
61 |
-
.wpo-plugin-title {
|
62 |
-
width:100%;
|
63 |
-
padding:30px 0 50px 0;
|
64 |
-
margin:0;
|
65 |
-
background-color:#fdfdfd;
|
66 |
-
text-align:center;
|
67 |
-
display:block;
|
68 |
-
color:#61707d;
|
69 |
-
}
|
70 |
-
|
71 |
-
.wpo-progress-bar {
|
72 |
-
width:100%;
|
73 |
-
height:36px;
|
74 |
-
overflow:hidden;
|
75 |
-
list-style: none;
|
76 |
-
margin:-18px 0 0 0;
|
77 |
-
padding:0;
|
78 |
-
}
|
79 |
-
|
80 |
-
.wpo-progress-bar li {
|
81 |
-
width:16.66%;
|
82 |
-
float:left;
|
83 |
-
box-sizing: border-box;
|
84 |
-
}
|
85 |
-
|
86 |
-
.wpo-progress-marker {
|
87 |
-
height:24px;
|
88 |
-
width:24px;
|
89 |
-
background-color:#fdfdfd;
|
90 |
-
border:6px solid #3e9b9a;
|
91 |
-
border-radius:50%;
|
92 |
-
margin:0 auto;
|
93 |
-
}
|
94 |
-
|
95 |
-
.wpo-progress-bar .completed {
|
96 |
-
background-color:#3e9b9a;
|
97 |
-
}
|
98 |
-
|
99 |
-
.wpo-progress-bar .active {
|
100 |
-
background-color:#2d7170;
|
101 |
-
}
|
102 |
-
|
103 |
-
.wpo-setup-content {
|
104 |
-
float:left;
|
105 |
-
width:100%;
|
106 |
-
overflow:hidden;
|
107 |
-
}
|
108 |
-
|
109 |
-
.wpo-step-description {
|
110 |
-
width:50%;
|
111 |
-
float:left;
|
112 |
-
padding:50px;
|
113 |
-
box-sizing: border-box;
|
114 |
-
}
|
115 |
-
|
116 |
-
.wpo-step-description ul {
|
117 |
-
padding-left:16px;
|
118 |
-
}
|
119 |
-
|
120 |
-
.wpo-setup-input {
|
121 |
-
width:50%;
|
122 |
-
float:left;
|
123 |
-
padding:50px 50px 50px 0;
|
124 |
-
box-sizing: border-box;
|
125 |
-
position:relative;
|
126 |
-
}
|
127 |
-
|
128 |
-
.wpo-setup-input select {
|
129 |
-
width:100%;
|
130 |
-
border:none;
|
131 |
-
font-size:1.4em;
|
132 |
-
background-color:#fdfdfd;
|
133 |
-
color:#444;
|
134 |
-
border:1px solid #3e9b9a;
|
135 |
-
height:40px;
|
136 |
-
}
|
137 |
-
|
138 |
-
.wpo-setup-buttons {
|
139 |
-
width:100%;
|
140 |
-
float:left;
|
141 |
-
padding:0px 50px 50px 50px;
|
142 |
-
box-sizing: border-box;
|
143 |
-
}
|
144 |
-
|
145 |
-
.wpo-button-next, .wpo-button-previous, .wpo-skip-step {
|
146 |
-
background-color:#3e9b9a;
|
147 |
-
border-radius:5px;
|
148 |
-
padding:10px 20px;
|
149 |
-
border:none;
|
150 |
-
box-sizing: border-box;
|
151 |
-
font-size:1em;
|
152 |
-
text-decoration: none;
|
153 |
-
cursor:pointer;
|
154 |
-
color:#fdfdfd;
|
155 |
-
font-size:1.4em;
|
156 |
-
box-sizing: border-box;
|
157 |
-
}
|
158 |
-
|
159 |
-
.wpo-button-next:hover, .wpo-button-previous:hover {
|
160 |
-
background-color:#2d7170;
|
161 |
-
}
|
162 |
-
|
163 |
-
.wpo-button-next, .wpo-skip-step {
|
164 |
-
float:right;
|
165 |
-
margin-left:10px;
|
166 |
-
}
|
167 |
-
|
168 |
-
.wpo-button-previous {
|
169 |
-
float:left;
|
170 |
-
}
|
171 |
-
|
172 |
-
.wpo-skip-step {
|
173 |
-
background-color:#fdfdfd;
|
174 |
-
color:#61707d;
|
175 |
-
}
|
176 |
-
|
177 |
-
.wpo-skip-step:hover {
|
178 |
-
color:#333;
|
179 |
-
}
|
180 |
-
|
181 |
-
/* Shop address */
|
182 |
-
|
183 |
-
.wpo-setup-input .shop-name {
|
184 |
-
width:100%;
|
185 |
-
height:40px;
|
186 |
-
padding:10px 15px;
|
187 |
-
box-sizing: border-box;
|
188 |
-
font-size:1.2em;
|
189 |
-
margin-bottom:20px;
|
190 |
-
border:1px solid #3e9b9a;
|
191 |
-
font-family: 'Ubuntu', sans-serif;
|
192 |
-
}
|
193 |
-
|
194 |
-
.wpo-setup-input .shop-address {
|
195 |
-
width:100%;
|
196 |
-
height:200px;
|
197 |
-
padding:15px 15px;
|
198 |
-
font-size:1.2em;
|
199 |
-
line-height:1.4em;
|
200 |
-
box-sizing: border-box;
|
201 |
-
border:1px solid #3e9b9a;
|
202 |
-
resize: none;
|
203 |
-
float:left;
|
204 |
-
margin:0;
|
205 |
-
font-family: 'Ubuntu', sans-serif;
|
206 |
-
text-align:left!important;
|
207 |
-
}
|
208 |
-
|
209 |
-
/* Your logo */
|
210 |
-
|
211 |
-
.wpo-setup-input #img-header_logo {
|
212 |
-
width:100%;
|
213 |
-
height:auto;
|
214 |
-
background:#fdfdfd;
|
215 |
-
margin-bottom:20px;
|
216 |
-
padding:20px;
|
217 |
-
box-sizing: border-box;
|
218 |
-
position:relative;
|
219 |
-
}
|
220 |
-
|
221 |
-
.wpo-setup-input #logo-preview img {
|
222 |
-
height:80%;
|
223 |
-
position:absolute;
|
224 |
-
left:50%;
|
225 |
-
-ms-transform: translate(-50%, 0%); /* IE 9 */
|
226 |
-
-webkit-transform: translate(-50%, 0%); /* Safari */
|
227 |
-
transform: translate(-50%, 0%);
|
228 |
-
}
|
229 |
-
|
230 |
-
/* Attach to */
|
231 |
-
|
232 |
-
.wpo-setup-input input[type=checkbox] {
|
233 |
-
-webkit-appearance: none;
|
234 |
-
background-color: #fdfdfd;
|
235 |
-
border: 1px solid #cacece;
|
236 |
-
box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px -15px 10px -12px rgba(0,0,0,0.05);
|
237 |
-
padding: 14px;
|
238 |
-
border-radius: 3px;
|
239 |
-
display: inline-block;
|
240 |
-
position: relative;
|
241 |
-
}
|
242 |
-
|
243 |
-
.wpo-setup-input input[type=checkbox]:active, .wpo-setup-input input[type=checkbox]:checked:active {
|
244 |
-
box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px 1px 3px rgba(0,0,0,0.1);
|
245 |
-
}
|
246 |
-
|
247 |
-
.wpo-setup-input input[type=checkbox]:checked {
|
248 |
-
background-color: #fdfdfd;
|
249 |
-
border: 1px solid #adb8c0;
|
250 |
-
box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px -15px 10px -12px rgba(0,0,0,0.05), inset 15px 10px -12px rgba(255,255,255,0.1);
|
251 |
-
color: #99a1a7;
|
252 |
-
}
|
253 |
-
|
254 |
-
.wpo-setup-input input[type=checkbox]:checked:after {
|
255 |
-
content: '\2714';
|
256 |
-
font-size: 24px;
|
257 |
-
position: absolute;
|
258 |
-
top: 0px;
|
259 |
-
left: 3px;
|
260 |
-
color: #3e9b9a;
|
261 |
-
}
|
262 |
-
|
263 |
-
.wpo-setup-input .checkbox {
|
264 |
-
font-size:1.2em;
|
265 |
-
margin-left:50px;
|
266 |
-
margin-top:-35px;
|
267 |
-
margin-bottom:-8px;
|
268 |
-
display:block;
|
269 |
-
}
|
270 |
-
|
271 |
-
/* Customizer */
|
272 |
-
|
273 |
-
.wpo-setup-input .how-to-box {
|
274 |
-
float:right;
|
275 |
-
width:auto;
|
276 |
-
max-width:100%;
|
277 |
-
height:auto;
|
278 |
-
border:1px solid #3e9b9a;
|
279 |
-
overflow:hidden;
|
280 |
-
background:#fff;
|
281 |
-
}
|
282 |
-
|
283 |
-
.wpo-setup-input .how-to-box img {
|
284 |
-
width:100%;
|
285 |
-
}
|
286 |
-
|
287 |
-
.wpo-step-description a:link, .wpo-step-description a:visited {
|
288 |
-
color:#3e9b9a;
|
289 |
-
text-decoration:none;
|
290 |
-
}
|
291 |
-
|
292 |
-
/* Good to go */
|
293 |
-
|
294 |
-
.wpo-final {
|
295 |
-
text-align:center;
|
296 |
-
width:100%;
|
297 |
-
padding:50px 200px;
|
298 |
-
}
|
299 |
-
|
300 |
-
.wpo-final h1 {
|
301 |
-
font-size:3em;
|
302 |
-
line-height:1em;
|
303 |
-
}
|
304 |
-
|
305 |
-
/* Portrait tablets and small desktops */
|
306 |
-
@media (min-width: 768px) and (max-width: 991px) {
|
307 |
-
.wpo-setup-card {
|
308 |
-
width:80%;
|
309 |
-
}
|
310 |
-
}
|
311 |
-
|
312 |
-
/* Landscape phones and portrait tablets */
|
313 |
-
@media (max-width: 767px) {
|
314 |
-
.wpo-setup-card {
|
315 |
-
width:100%;
|
316 |
-
min-height:100%;
|
317 |
-
border-radius:0px;
|
318 |
-
margin:0;
|
319 |
-
}
|
320 |
-
|
321 |
-
.wpo-step-description, .wpo-setup-input {
|
322 |
-
width:100%;
|
323 |
-
padding-left:50px;
|
324 |
-
}
|
325 |
-
|
326 |
-
.wpo-final {
|
327 |
-
padding:50px;
|
328 |
-
}
|
329 |
-
|
330 |
-
.wpo-progress-bar {
|
331 |
-
margin-top:-12px;
|
332 |
-
}
|
333 |
-
|
334 |
-
.wpo-progress-marker {
|
335 |
-
height:16px;
|
336 |
-
width:16px;
|
337 |
-
border-width:4px;
|
338 |
-
}
|
339 |
-
|
340 |
-
.wpo-button-next, .wpo-button-previous, .wpo-skip-step {
|
341 |
-
font-size:1em;
|
342 |
-
padding:8px 12px;
|
343 |
-
}
|
344 |
-
}
|
345 |
-
|
346 |
-
/* Landscape phones and smaller */
|
347 |
-
@media (max-width: 480px) {
|
348 |
-
|
349 |
-
.wpo-wizzard, .wpo-wcpdf-setup {
|
350 |
-
margin:0;
|
351 |
-
height:100%;
|
352 |
-
}
|
353 |
-
|
354 |
-
.wpo-wcpdf-setup form {
|
355 |
-
height:100%;
|
356 |
-
}
|
357 |
-
|
358 |
-
.wpo-setup-card {
|
359 |
-
width:100%;
|
360 |
-
min-height:100%;
|
361 |
-
border-radius:0px;
|
362 |
-
margin:0;
|
363 |
-
}
|
364 |
-
|
365 |
-
.wpo-plugin-title {
|
366 |
-
padding:25px 0 30px 0;
|
367 |
-
font-size:1.4em;
|
368 |
-
}
|
369 |
-
|
370 |
-
.wpo-step-description, .wpo-setup-input, .wpo-setup-buttons {
|
371 |
-
width:100%;
|
372 |
-
padding:20px;
|
373 |
-
}
|
374 |
-
|
375 |
-
.wpo-step-description {
|
376 |
-
padding-bottom:0;
|
377 |
-
}
|
378 |
-
|
379 |
-
.wpo-progress-bar {
|
380 |
-
margin-top:-12px;
|
381 |
-
}
|
382 |
-
|
383 |
-
.wpo-progress-marker {
|
384 |
-
height:16px;
|
385 |
-
width:16px;
|
386 |
-
border-width:4px;
|
387 |
-
}
|
388 |
-
|
389 |
-
.wpo-button-next, .wpo-button-previous, .wpo-skip-step {
|
390 |
-
font-size:1em;
|
391 |
-
padding:8px 12px;
|
392 |
-
}
|
393 |
-
|
394 |
-
.wpo-setup-input .checkbox {
|
395 |
-
font-size:1em;
|
396 |
-
}
|
397 |
-
}
|
398 |
-
|
399 |
-
|
1 |
+
.wpo-wizzard {
|
2 |
+
background:#f9f9f9;
|
3 |
+
}
|
4 |
+
|
5 |
+
.wpo-wcpdf-setup {
|
6 |
+
padding:0;
|
7 |
+
font-family: 'Ubuntu', sans-serif;
|
8 |
+
font-size: 14px;
|
9 |
+
line-height: 22px;
|
10 |
+
color:#444;
|
11 |
+
background:none;
|
12 |
+
-webkit-box-shadow: none;
|
13 |
+
box-shadow: none;
|
14 |
+
}
|
15 |
+
|
16 |
+
.wpo-wcpdf-setup h1, .wpo-wcpdf-setup h2 {
|
17 |
+
border:none;
|
18 |
+
color:#444;
|
19 |
+
margin:0;
|
20 |
+
}
|
21 |
+
|
22 |
+
#confetti {
|
23 |
+
width:100%;
|
24 |
+
height:100%;
|
25 |
+
position:absolute;
|
26 |
+
left:0;
|
27 |
+
top:0;
|
28 |
+
z-index:-999;
|
29 |
+
}
|
30 |
+
|
31 |
+
.wpo-setup {
|
32 |
+
background-color:#f9f9f9;
|
33 |
+
font-family: 'Ubuntu', sans-serif;
|
34 |
+
font-size: 14px;
|
35 |
+
line-height: 22px;
|
36 |
+
color:#444;
|
37 |
+
width:100%;
|
38 |
+
height:100%;
|
39 |
+
margin:0;
|
40 |
+
padding:0;
|
41 |
+
}
|
42 |
+
|
43 |
+
.confetti {
|
44 |
+
width:100%;
|
45 |
+
height:100%;
|
46 |
+
z-index:-999;
|
47 |
+
position:relative;
|
48 |
+
}
|
49 |
+
|
50 |
+
.wpo-setup-card {
|
51 |
+
display:block;
|
52 |
+
background-color:#f2fafa;
|
53 |
+
max-width:900px;
|
54 |
+
margin:100px auto 100px auto;
|
55 |
+
padding:0;
|
56 |
+
border-radius:10px;
|
57 |
+
box-shadow:10px 10px 80px rgba(0,0,0,0.05);
|
58 |
+
overflow:hidden;
|
59 |
+
}
|
60 |
+
|
61 |
+
.wpo-plugin-title {
|
62 |
+
width:100%;
|
63 |
+
padding:30px 0 50px 0;
|
64 |
+
margin:0;
|
65 |
+
background-color:#fdfdfd;
|
66 |
+
text-align:center;
|
67 |
+
display:block;
|
68 |
+
color:#61707d;
|
69 |
+
}
|
70 |
+
|
71 |
+
.wpo-progress-bar {
|
72 |
+
width:100%;
|
73 |
+
height:36px;
|
74 |
+
overflow:hidden;
|
75 |
+
list-style: none;
|
76 |
+
margin:-18px 0 0 0;
|
77 |
+
padding:0;
|
78 |
+
}
|
79 |
+
|
80 |
+
.wpo-progress-bar li {
|
81 |
+
width:16.66%;
|
82 |
+
float:left;
|
83 |
+
box-sizing: border-box;
|
84 |
+
}
|
85 |
+
|
86 |
+
.wpo-progress-marker {
|
87 |
+
height:24px;
|
88 |
+
width:24px;
|
89 |
+
background-color:#fdfdfd;
|
90 |
+
border:6px solid #3e9b9a;
|
91 |
+
border-radius:50%;
|
92 |
+
margin:0 auto;
|
93 |
+
}
|
94 |
+
|
95 |
+
.wpo-progress-bar .completed {
|
96 |
+
background-color:#3e9b9a;
|
97 |
+
}
|
98 |
+
|
99 |
+
.wpo-progress-bar .active {
|
100 |
+
background-color:#2d7170;
|
101 |
+
}
|
102 |
+
|
103 |
+
.wpo-setup-content {
|
104 |
+
float:left;
|
105 |
+
width:100%;
|
106 |
+
overflow:hidden;
|
107 |
+
}
|
108 |
+
|
109 |
+
.wpo-step-description {
|
110 |
+
width:50%;
|
111 |
+
float:left;
|
112 |
+
padding:50px;
|
113 |
+
box-sizing: border-box;
|
114 |
+
}
|
115 |
+
|
116 |
+
.wpo-step-description ul {
|
117 |
+
padding-left:16px;
|
118 |
+
}
|
119 |
+
|
120 |
+
.wpo-setup-input {
|
121 |
+
width:50%;
|
122 |
+
float:left;
|
123 |
+
padding:50px 50px 50px 0;
|
124 |
+
box-sizing: border-box;
|
125 |
+
position:relative;
|
126 |
+
}
|
127 |
+
|
128 |
+
.wpo-setup-input select {
|
129 |
+
width:100%;
|
130 |
+
border:none;
|
131 |
+
font-size:1.4em;
|
132 |
+
background-color:#fdfdfd;
|
133 |
+
color:#444;
|
134 |
+
border:1px solid #3e9b9a;
|
135 |
+
height:40px;
|
136 |
+
}
|
137 |
+
|
138 |
+
.wpo-setup-buttons {
|
139 |
+
width:100%;
|
140 |
+
float:left;
|
141 |
+
padding:0px 50px 50px 50px;
|
142 |
+
box-sizing: border-box;
|
143 |
+
}
|
144 |
+
|
145 |
+
.wpo-button-next, .wpo-button-previous, .wpo-skip-step {
|
146 |
+
background-color:#3e9b9a;
|
147 |
+
border-radius:5px;
|
148 |
+
padding:10px 20px;
|
149 |
+
border:none;
|
150 |
+
box-sizing: border-box;
|
151 |
+
font-size:1em;
|
152 |
+
text-decoration: none;
|
153 |
+
cursor:pointer;
|
154 |
+
color:#fdfdfd;
|
155 |
+
font-size:1.4em;
|
156 |
+
box-sizing: border-box;
|
157 |
+
}
|
158 |
+
|
159 |
+
.wpo-button-next:hover, .wpo-button-previous:hover {
|
160 |
+
background-color:#2d7170;
|
161 |
+
}
|
162 |
+
|
163 |
+
.wpo-button-next, .wpo-skip-step {
|
164 |
+
float:right;
|
165 |
+
margin-left:10px;
|
166 |
+
}
|
167 |
+
|
168 |
+
.wpo-button-previous {
|
169 |
+
float:left;
|
170 |
+
}
|
171 |
+
|
172 |
+
.wpo-skip-step {
|
173 |
+
background-color:#fdfdfd;
|
174 |
+
color:#61707d;
|
175 |
+
}
|
176 |
+
|
177 |
+
.wpo-skip-step:hover {
|
178 |
+
color:#333;
|
179 |
+
}
|
180 |
+
|
181 |
+
/* Shop address */
|
182 |
+
|
183 |
+
.wpo-setup-input .shop-name {
|
184 |
+
width:100%;
|
185 |
+
height:40px;
|
186 |
+
padding:10px 15px;
|
187 |
+
box-sizing: border-box;
|
188 |
+
font-size:1.2em;
|
189 |
+
margin-bottom:20px;
|
190 |
+
border:1px solid #3e9b9a;
|
191 |
+
font-family: 'Ubuntu', sans-serif;
|
192 |
+
}
|
193 |
+
|
194 |
+
.wpo-setup-input .shop-address {
|
195 |
+
width:100%;
|
196 |
+
height:200px;
|
197 |
+
padding:15px 15px;
|
198 |
+
font-size:1.2em;
|
199 |
+
line-height:1.4em;
|
200 |
+
box-sizing: border-box;
|
201 |
+
border:1px solid #3e9b9a;
|
202 |
+
resize: none;
|
203 |
+
float:left;
|
204 |
+
margin:0;
|
205 |
+
font-family: 'Ubuntu', sans-serif;
|
206 |
+
text-align:left!important;
|
207 |
+
}
|
208 |
+
|
209 |
+
/* Your logo */
|
210 |
+
|
211 |
+
.wpo-setup-input #img-header_logo {
|
212 |
+
width:100%;
|
213 |
+
height:auto;
|
214 |
+
background:#fdfdfd;
|
215 |
+
margin-bottom:20px;
|
216 |
+
padding:20px;
|
217 |
+
box-sizing: border-box;
|
218 |
+
position:relative;
|
219 |
+
}
|
220 |
+
|
221 |
+
.wpo-setup-input #logo-preview img {
|
222 |
+
height:80%;
|
223 |
+
position:absolute;
|
224 |
+
left:50%;
|
225 |
+
-ms-transform: translate(-50%, 0%); /* IE 9 */
|
226 |
+
-webkit-transform: translate(-50%, 0%); /* Safari */
|
227 |
+
transform: translate(-50%, 0%);
|
228 |
+
}
|
229 |
+
|
230 |
+
/* Attach to */
|
231 |
+
|
232 |
+
.wpo-setup-input input[type=checkbox] {
|
233 |
+
-webkit-appearance: none;
|
234 |
+
background-color: #fdfdfd;
|
235 |
+
border: 1px solid #cacece;
|
236 |
+
box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px -15px 10px -12px rgba(0,0,0,0.05);
|
237 |
+
padding: 14px;
|
238 |
+
border-radius: 3px;
|
239 |
+
display: inline-block;
|
240 |
+
position: relative;
|
241 |
+
}
|
242 |
+
|
243 |
+
.wpo-setup-input input[type=checkbox]:active, .wpo-setup-input input[type=checkbox]:checked:active {
|
244 |
+
box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px 1px 3px rgba(0,0,0,0.1);
|
245 |
+
}
|
246 |
+
|
247 |
+
.wpo-setup-input input[type=checkbox]:checked {
|
248 |
+
background-color: #fdfdfd;
|
249 |
+
border: 1px solid #adb8c0;
|
250 |
+
box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px -15px 10px -12px rgba(0,0,0,0.05), inset 15px 10px -12px rgba(255,255,255,0.1);
|
251 |
+
color: #99a1a7;
|
252 |
+
}
|
253 |
+
|
254 |
+
.wpo-setup-input input[type=checkbox]:checked:after {
|
255 |
+
content: '\2714';
|
256 |
+
font-size: 24px;
|
257 |
+
position: absolute;
|
258 |
+
top: 0px;
|
259 |
+
left: 3px;
|
260 |
+
color: #3e9b9a;
|
261 |
+
}
|
262 |
+
|
263 |
+
.wpo-setup-input .checkbox {
|
264 |
+
font-size:1.2em;
|
265 |
+
margin-left:50px;
|
266 |
+
margin-top:-35px;
|
267 |
+
margin-bottom:-8px;
|
268 |
+
display:block;
|
269 |
+
}
|
270 |
+
|
271 |
+
/* Customizer */
|
272 |
+
|
273 |
+
.wpo-setup-input .how-to-box {
|
274 |
+
float:right;
|
275 |
+
width:auto;
|
276 |
+
max-width:100%;
|
277 |
+
height:auto;
|
278 |
+
border:1px solid #3e9b9a;
|
279 |
+
overflow:hidden;
|
280 |
+
background:#fff;
|
281 |
+
}
|
282 |
+
|
283 |
+
.wpo-setup-input .how-to-box img {
|
284 |
+
width:100%;
|
285 |
+
}
|
286 |
+
|
287 |
+
.wpo-step-description a:link, .wpo-step-description a:visited {
|
288 |
+
color:#3e9b9a;
|
289 |
+
text-decoration:none;
|
290 |
+
}
|
291 |
+
|
292 |
+
/* Good to go */
|
293 |
+
|
294 |
+
.wpo-final {
|
295 |
+
text-align:center;
|
296 |
+
width:100%;
|
297 |
+
padding:50px 200px;
|
298 |
+
}
|
299 |
+
|
300 |
+
.wpo-final h1 {
|
301 |
+
font-size:3em;
|
302 |
+
line-height:1em;
|
303 |
+
}
|
304 |
+
|
305 |
+
/* Portrait tablets and small desktops */
|
306 |
+
@media (min-width: 768px) and (max-width: 991px) {
|
307 |
+
.wpo-setup-card {
|
308 |
+
width:80%;
|
309 |
+
}
|
310 |
+
}
|
311 |
+
|
312 |
+
/* Landscape phones and portrait tablets */
|
313 |
+
@media (max-width: 767px) {
|
314 |
+
.wpo-setup-card {
|
315 |
+
width:100%;
|
316 |
+
min-height:100%;
|
317 |
+
border-radius:0px;
|
318 |
+
margin:0;
|
319 |
+
}
|
320 |
+
|
321 |
+
.wpo-step-description, .wpo-setup-input {
|
322 |
+
width:100%;
|
323 |
+
padding-left:50px;
|
324 |
+
}
|
325 |
+
|
326 |
+
.wpo-final {
|
327 |
+
padding:50px;
|
328 |
+
}
|
329 |
+
|
330 |
+
.wpo-progress-bar {
|
331 |
+
margin-top:-12px;
|
332 |
+
}
|
333 |
+
|
334 |
+
.wpo-progress-marker {
|
335 |
+
height:16px;
|
336 |
+
width:16px;
|
337 |
+
border-width:4px;
|
338 |
+
}
|
339 |
+
|
340 |
+
.wpo-button-next, .wpo-button-previous, .wpo-skip-step {
|
341 |
+
font-size:1em;
|
342 |
+
padding:8px 12px;
|
343 |
+
}
|
344 |
+
}
|
345 |
+
|
346 |
+
/* Landscape phones and smaller */
|
347 |
+
@media (max-width: 480px) {
|
348 |
+
|
349 |
+
.wpo-wizzard, .wpo-wcpdf-setup {
|
350 |
+
margin:0;
|
351 |
+
height:100%;
|
352 |
+
}
|
353 |
+
|
354 |
+
.wpo-wcpdf-setup form {
|
355 |
+
height:100%;
|
356 |
+
}
|
357 |
+
|
358 |
+
.wpo-setup-card {
|
359 |
+
width:100%;
|
360 |
+
min-height:100%;
|
361 |
+
border-radius:0px;
|
362 |
+
margin:0;
|
363 |
+
}
|
364 |
+
|
365 |
+
.wpo-plugin-title {
|
366 |
+
padding:25px 0 30px 0;
|
367 |
+
font-size:1.4em;
|
368 |
+
}
|
369 |
+
|
370 |
+
.wpo-step-description, .wpo-setup-input, .wpo-setup-buttons {
|
371 |
+
width:100%;
|
372 |
+
padding:20px;
|
373 |
+
}
|
374 |
+
|
375 |
+
.wpo-step-description {
|
376 |
+
padding-bottom:0;
|
377 |
+
}
|
378 |
+
|
379 |
+
.wpo-progress-bar {
|
380 |
+
margin-top:-12px;
|
381 |
+
}
|
382 |
+
|
383 |
+
.wpo-progress-marker {
|
384 |
+
height:16px;
|
385 |
+
width:16px;
|
386 |
+
border-width:4px;
|
387 |
+
}
|
388 |
+
|
389 |
+
.wpo-button-next, .wpo-button-previous, .wpo-skip-step {
|
390 |
+
font-size:1em;
|
391 |
+
padding:8px 12px;
|
392 |
+
}
|
393 |
+
|
394 |
+
.wpo-setup-input .checkbox {
|
395 |
+
font-size:1em;
|
396 |
+
}
|
397 |
+
}
|
398 |
+
|
399 |
+
|
assets/js/confetti.js
CHANGED
@@ -1,348 +1,348 @@
|
|
1 |
-
jQuery(document).ready(function($) {
|
2 |
-
var frameRate = 30;
|
3 |
-
var dt = 1.0 / frameRate;
|
4 |
-
var DEG_TO_RAD = Math.PI / 180;
|
5 |
-
var RAD_TO_DEG = 180 / Math.PI;
|
6 |
-
var colors = [
|
7 |
-
["#df0049", "#660671"],
|
8 |
-
["#00e857", "#005291"],
|
9 |
-
["#2bebbc", "#05798a"],
|
10 |
-
["#ffd200", "#b06c00"]
|
11 |
-
];
|
12 |
-
|
13 |
-
function Vector2(_x, _y) {
|
14 |
-
this.x = _x, this.y = _y;
|
15 |
-
this.Length = function() {
|
16 |
-
return Math.sqrt(this.SqrLength());
|
17 |
-
}
|
18 |
-
this.SqrLength = function() {
|
19 |
-
return this.x * this.x + this.y * this.y;
|
20 |
-
}
|
21 |
-
this.Equals = function(_vec0, _vec1) {
|
22 |
-
return _vec0.x == _vec1.x && _vec0.y == _vec1.y;
|
23 |
-
}
|
24 |
-
this.Add = function(_vec) {
|
25 |
-
this.x += _vec.x;
|
26 |
-
this.y += _vec.y;
|
27 |
-
}
|
28 |
-
this.Sub = function(_vec) {
|
29 |
-
this.x -= _vec.x;
|
30 |
-
this.y -= _vec.y;
|
31 |
-
}
|
32 |
-
this.Div = function(_f) {
|
33 |
-
this.x /= _f;
|
34 |
-
this.y /= _f;
|
35 |
-
}
|
36 |
-
this.Mul = function(_f) {
|
37 |
-
this.x *= _f;
|
38 |
-
this.y *= _f;
|
39 |
-
}
|
40 |
-
this.Normalize = function() {
|
41 |
-
var sqrLen = this.SqrLength();
|
42 |
-
if (sqrLen != 0) {
|
43 |
-
var factor = 1.0 / Math.sqrt(sqrLen);
|
44 |
-
this.x *= factor;
|
45 |
-
this.y *= factor;
|
46 |
-
}
|
47 |
-
}
|
48 |
-
this.Normalized = function() {
|
49 |
-
var sqrLen = this.SqrLength();
|
50 |
-
if (sqrLen != 0) {
|
51 |
-
var factor = 1.0 / Math.sqrt(sqrLen);
|
52 |
-
return new Vector2(this.x * factor, this.y * factor);
|
53 |
-
}
|
54 |
-
return new Vector2(0, 0);
|
55 |
-
}
|
56 |
-
}
|
57 |
-
Vector2.Lerp = function(_vec0, _vec1, _t) {
|
58 |
-
return new Vector2((_vec1.x - _vec0.x) * _t + _vec0.x, (_vec1.y - _vec0.y) * _t + _vec0.y);
|
59 |
-
}
|
60 |
-
Vector2.Distance = function(_vec0, _vec1) {
|
61 |
-
return Math.sqrt(Vector2.SqrDistance(_vec0, _vec1));
|
62 |
-
}
|
63 |
-
Vector2.SqrDistance = function(_vec0, _vec1) {
|
64 |
-
var x = _vec0.x - _vec1.x;
|
65 |
-
var y = _vec0.y - _vec1.y;
|
66 |
-
return (x * x + y * y + z * z);
|
67 |
-
}
|
68 |
-
Vector2.Scale = function(_vec0, _vec1) {
|
69 |
-
return new Vector2(_vec0.x * _vec1.x, _vec0.y * _vec1.y);
|
70 |
-
}
|
71 |
-
Vector2.Min = function(_vec0, _vec1) {
|
72 |
-
return new Vector2(Math.min(_vec0.x, _vec1.x), Math.min(_vec0.y, _vec1.y));
|
73 |
-
}
|
74 |
-
Vector2.Max = function(_vec0, _vec1) {
|
75 |
-
return new Vector2(Math.max(_vec0.x, _vec1.x), Math.max(_vec0.y, _vec1.y));
|
76 |
-
}
|
77 |
-
Vector2.ClampMagnitude = function(_vec0, _len) {
|
78 |
-
var vecNorm = _vec0.Normalized;
|
79 |
-
return new Vector2(vecNorm.x * _len, vecNorm.y * _len);
|
80 |
-
}
|
81 |
-
Vector2.Sub = function(_vec0, _vec1) {
|
82 |
-
return new Vector2(_vec0.x - _vec1.x, _vec0.y - _vec1.y, _vec0.z - _vec1.z);
|
83 |
-
}
|
84 |
-
|
85 |
-
function EulerMass(_x, _y, _mass, _drag) {
|
86 |
-
this.position = new Vector2(_x, _y);
|
87 |
-
this.mass = _mass;
|
88 |
-
this.drag = _drag;
|
89 |
-
this.force = new Vector2(0, 0);
|
90 |
-
this.velocity = new Vector2(0, 0);
|
91 |
-
this.AddForce = function(_f) {
|
92 |
-
this.force.Add(_f);
|
93 |
-
}
|
94 |
-
this.Integrate = function(_dt) {
|
95 |
-
var acc = this.CurrentForce(this.position);
|
96 |
-
acc.Div(this.mass);
|
97 |
-
var posDelta = new Vector2(this.velocity.x, this.velocity.y);
|
98 |
-
posDelta.Mul(_dt);
|
99 |
-
this.position.Add(posDelta);
|
100 |
-
acc.Mul(_dt);
|
101 |
-
this.velocity.Add(acc);
|
102 |
-
this.force = new Vector2(0, 0);
|
103 |
-
}
|
104 |
-
this.CurrentForce = function(_pos, _vel) {
|
105 |
-
var totalForce = new Vector2(this.force.x, this.force.y);
|
106 |
-
var speed = this.velocity.Length();
|
107 |
-
var dragVel = new Vector2(this.velocity.x, this.velocity.y);
|
108 |
-
dragVel.Mul(this.drag * this.mass * speed);
|
109 |
-
totalForce.Sub(dragVel);
|
110 |
-
return totalForce;
|
111 |
-
}
|
112 |
-
}
|
113 |
-
|
114 |
-
function ConfettiPaper(_x, _y) {
|
115 |
-
this.pos = new Vector2(_x, _y);
|
116 |
-
this.rotationSpeed = Math.random() * 600 + 800;
|
117 |
-
this.angle = DEG_TO_RAD * Math.random() * 360;
|
118 |
-
this.rotation = DEG_TO_RAD * Math.random() * 360;
|
119 |
-
this.cosA = 1.0;
|
120 |
-
this.size = 5.0;
|
121 |
-
this.oscillationSpeed = Math.random() * 1.5 + 0.5;
|
122 |
-
this.xSpeed = 40.0;
|
123 |
-
this.ySpeed = Math.random() * 60 + 50.0;
|
124 |
-
this.corners = new Array();
|
125 |
-
this.time = Math.random();
|
126 |
-
var ci = Math.round(Math.random() * (colors.length - 1));
|
127 |
-
this.frontColor = colors[ci][0];
|
128 |
-
this.backColor = colors[ci][1];
|
129 |
-
for (var i = 0; i < 4; i++) {
|
130 |
-
var dx = Math.cos(this.angle + DEG_TO_RAD * (i * 90 + 45));
|
131 |
-
var dy = Math.sin(this.angle + DEG_TO_RAD * (i * 90 + 45));
|
132 |
-
this.corners[i] = new Vector2(dx, dy);
|
133 |
-
}
|
134 |
-
this.Update = function(_dt) {
|
135 |
-
this.time += _dt;
|
136 |
-
this.rotation += this.rotationSpeed * _dt;
|
137 |
-
this.cosA = Math.cos(DEG_TO_RAD * this.rotation);
|
138 |
-
this.pos.x += Math.cos(this.time * this.oscillationSpeed) * this.xSpeed * _dt
|
139 |
-
this.pos.y += this.ySpeed * _dt;
|
140 |
-
if (this.pos.y > ConfettiPaper.bounds.y) {
|
141 |
-
this.pos.x = Math.random() * ConfettiPaper.bounds.x;
|
142 |
-
this.pos.y = 0;
|
143 |
-
}
|
144 |
-
}
|
145 |
-
this.Draw = function(_g) {
|
146 |
-
if (this.cosA > 0) {
|
147 |
-
_g.fillStyle = this.frontColor;
|
148 |
-
} else {
|
149 |
-
_g.fillStyle = this.backColor;
|
150 |
-
}
|
151 |
-
_g.beginPath();
|
152 |
-
_g.moveTo(this.pos.x + this.corners[0].x * this.size, this.pos.y + this.corners[0].y * this.size * this.cosA);
|
153 |
-
for (var i = 1; i < 4; i++) {
|
154 |
-
_g.lineTo(this.pos.x + this.corners[i].x * this.size, this.pos.y + this.corners[i].y * this.size * this.cosA);
|
155 |
-
}
|
156 |
-
_g.closePath();
|
157 |
-
_g.fill();
|
158 |
-
}
|
159 |
-
}
|
160 |
-
ConfettiPaper.bounds = new Vector2(0, 0);
|
161 |
-
|
162 |
-
function ConfettiRibbon(_x, _y, _count, _dist, _thickness, _angle, _mass, _drag) {
|
163 |
-
this.particleDist = _dist;
|
164 |
-
this.particleCount = _count;
|
165 |
-
this.particleMass = _mass;
|
166 |
-
this.particleDrag = _drag;
|
167 |
-
this.particles = new Array();
|
168 |
-
var ci = Math.round(Math.random() * (colors.length - 1));
|
169 |
-
this.frontColor = colors[ci][0];
|
170 |
-
this.backColor = colors[ci][1];
|
171 |
-
this.xOff = Math.cos(DEG_TO_RAD * _angle) * _thickness;
|
172 |
-
this.yOff = Math.sin(DEG_TO_RAD * _angle) * _thickness;
|
173 |
-
this.position = new Vector2(_x, _y);
|
174 |
-
this.prevPosition = new Vector2(_x, _y);
|
175 |
-
this.velocityInherit = Math.random() * 2 + 4;
|
176 |
-
this.time = Math.random() * 100;
|
177 |
-
this.oscillationSpeed = Math.random() * 2 + 2;
|
178 |
-
this.oscillationDistance = Math.random() * 40 + 40;
|
179 |
-
this.ySpeed = Math.random() * 40 + 80;
|
180 |
-
for (var i = 0; i < this.particleCount; i++) {
|
181 |
-
this.particles[i] = new EulerMass(_x, _y - i * this.particleDist, this.particleMass, this.particleDrag);
|
182 |
-
}
|
183 |
-
this.Update = function(_dt) {
|
184 |
-
var i = 0;
|
185 |
-
this.time += _dt * this.oscillationSpeed;
|
186 |
-
this.position.y += this.ySpeed * _dt;
|
187 |
-
this.position.x += Math.cos(this.time) * this.oscillationDistance * _dt;
|
188 |
-
this.particles[0].position = this.position;
|
189 |
-
var dX = this.prevPosition.x - this.position.x;
|
190 |
-
var dY = this.prevPosition.y - this.position.y;
|
191 |
-
var delta = Math.sqrt(dX * dX + dY * dY);
|
192 |
-
this.prevPosition = new Vector2(this.position.x, this.position.y);
|
193 |
-
for (i = 1; i < this.particleCount; i++) {
|
194 |
-
var dirP = Vector2.Sub(this.particles[i - 1].position, this.particles[i].position);
|
195 |
-
dirP.Normalize();
|
196 |
-
dirP.Mul((delta / _dt) * this.velocityInherit);
|
197 |
-
this.particles[i].AddForce(dirP);
|
198 |
-
}
|
199 |
-
for (i = 1; i < this.particleCount; i++) {
|
200 |
-
this.particles[i].Integrate(_dt);
|
201 |
-
}
|
202 |
-
for (i = 1; i < this.particleCount; i++) {
|
203 |
-
var rp2 = new Vector2(this.particles[i].position.x, this.particles[i].position.y);
|
204 |
-
rp2.Sub(this.particles[i - 1].position);
|
205 |
-
rp2.Normalize();
|
206 |
-
rp2.Mul(this.particleDist);
|
207 |
-
rp2.Add(this.particles[i - 1].position);
|
208 |
-
this.particles[i].position = rp2;
|
209 |
-
}
|
210 |
-
if (this.position.y > ConfettiRibbon.bounds.y + this.particleDist * this.particleCount) {
|
211 |
-
this.Reset();
|
212 |
-
}
|
213 |
-
}
|
214 |
-
this.Reset = function() {
|
215 |
-
this.position.y = -Math.random() * ConfettiRibbon.bounds.y;
|
216 |
-
this.position.x = Math.random() * ConfettiRibbon.bounds.x;
|
217 |
-
this.prevPosition = new Vector2(this.position.x, this.position.y);
|
218 |
-
this.velocityInherit = Math.random() * 2 + 4;
|
219 |
-
this.time = Math.random() * 100;
|
220 |
-
this.oscillationSpeed = Math.random() * 2.0 + 1.5;
|
221 |
-
this.oscillationDistance = Math.random() * 40 + 40;
|
222 |
-
this.ySpeed = Math.random() * 40 + 80;
|
223 |
-
var ci = Math.round(Math.random() * (colors.length - 1));
|
224 |
-
this.frontColor = colors[ci][0];
|
225 |
-
this.backColor = colors[ci][1];
|
226 |
-
this.particles = new Array();
|
227 |
-
for (var i = 0; i < this.particleCount; i++) {
|
228 |
-
this.particles[i] = new EulerMass(this.position.x, this.position.y - i * this.particleDist, this.particleMass, this.particleDrag);
|
229 |
-
}
|
230 |
-
}
|
231 |
-
this.Draw = function(_g) {
|
232 |
-
for (var i = 0; i < this.particleCount - 1; i++) {
|
233 |
-
var p0 = new Vector2(this.particles[i].position.x + this.xOff, this.particles[i].position.y + this.yOff);
|
234 |
-
var p1 = new Vector2(this.particles[i + 1].position.x + this.xOff, this.particles[i + 1].position.y + this.yOff);
|
235 |
-
if (this.Side(this.particles[i].position.x, this.particles[i].position.y, this.particles[i + 1].position.x, this.particles[i + 1].position.y, p1.x, p1.y) < 0) {
|
236 |
-
_g.fillStyle = this.frontColor;
|
237 |
-
_g.strokeStyle = this.frontColor;
|
238 |
-
} else {
|
239 |
-
_g.fillStyle = this.backColor;
|
240 |
-
_g.strokeStyle = this.backColor;
|
241 |
-
}
|
242 |
-
if (i == 0) {
|
243 |
-
_g.beginPath();
|
244 |
-
_g.moveTo(this.particles[i].position.x, this.particles[i].position.y);
|
245 |
-
_g.lineTo(this.particles[i + 1].position.x, this.particles[i + 1].position.y);
|
246 |
-
_g.lineTo((this.particles[i + 1].position.x + p1.x) * 0.5, (this.particles[i + 1].position.y + p1.y) * 0.5);
|
247 |
-
_g.closePath();
|
248 |
-
_g.stroke();
|
249 |
-
_g.fill();
|
250 |
-
_g.beginPath();
|
251 |
-
_g.moveTo(p1.x, p1.y);
|
252 |
-
_g.lineTo(p0.x, p0.y);
|
253 |
-
_g.lineTo((this.particles[i + 1].position.x + p1.x) * 0.5, (this.particles[i + 1].position.y + p1.y) * 0.5);
|
254 |
-
_g.closePath();
|
255 |
-
_g.stroke();
|
256 |
-
_g.fill();
|
257 |
-
} else if (i == this.particleCount - 2) {
|
258 |
-
_g.beginPath();
|
259 |
-
_g.moveTo(this.particles[i].position.x, this.particles[i].position.y);
|
260 |
-
_g.lineTo(this.particles[i + 1].position.x, this.particles[i + 1].position.y);
|
261 |
-
_g.lineTo((this.particles[i].position.x + p0.x) * 0.5, (this.particles[i].position.y + p0.y) * 0.5);
|
262 |
-
_g.closePath();
|
263 |
-
_g.stroke();
|
264 |
-
_g.fill();
|
265 |
-
_g.beginPath();
|
266 |
-
_g.moveTo(p1.x, p1.y);
|
267 |
-
_g.lineTo(p0.x, p0.y);
|
268 |
-
_g.lineTo((this.particles[i].position.x + p0.x) * 0.5, (this.particles[i].position.y + p0.y) * 0.5);
|
269 |
-
_g.closePath();
|
270 |
-
_g.stroke();
|
271 |
-
_g.fill();
|
272 |
-
} else {
|
273 |
-
_g.beginPath();
|
274 |
-
_g.moveTo(this.particles[i].position.x, this.particles[i].position.y);
|
275 |
-
_g.lineTo(this.particles[i + 1].position.x, this.particles[i + 1].position.y);
|
276 |
-
_g.lineTo(p1.x, p1.y);
|
277 |
-
_g.lineTo(p0.x, p0.y);
|
278 |
-
_g.closePath();
|
279 |
-
_g.stroke();
|
280 |
-
_g.fill();
|
281 |
-
}
|
282 |
-
}
|
283 |
-
}
|
284 |
-
this.Side = function(x1, y1, x2, y2, x3, y3) {
|
285 |
-
return ((x1 - x2) * (y3 - y2) - (y1 - y2) * (x3 - x2));
|
286 |
-
}
|
287 |
-
}
|
288 |
-
ConfettiRibbon.bounds = new Vector2(0, 0);
|
289 |
-
confetti = {};
|
290 |
-
confetti.Context = function(parent) {
|
291 |
-
var i = 0;
|
292 |
-
var canvasParent = document.getElementById(parent);
|
293 |
-
var canvas = document.createElement('canvas');
|
294 |
-
canvas.width = canvasParent.offsetWidth;
|
295 |
-
canvas.height = canvasParent.offsetHeight;
|
296 |
-
canvasParent.appendChild(canvas);
|
297 |
-
var context = canvas.getContext('2d');
|
298 |
-
var interval = null;
|
299 |
-
var confettiRibbonCount = 7;
|
300 |
-
var rpCount = 30;
|
301 |
-
var rpDist = 8.0;
|
302 |
-
var rpThick = 8.0;
|
303 |
-
var confettiRibbons = new Array();
|
304 |
-
ConfettiRibbon.bounds = new Vector2(canvas.width, canvas.height);
|
305 |
-
for (i = 0; i < confettiRibbonCount; i++) {
|
306 |
-
confettiRibbons[i] = new ConfettiRibbon(Math.random() * canvas.width, -Math.random() * canvas.height * 2, rpCount, rpDist, rpThick, 45, 1, 0.05);
|
307 |
-
}
|
308 |
-
var confettiPaperCount = 25;
|
309 |
-
var confettiPapers = new Array();
|
310 |
-
ConfettiPaper.bounds = new Vector2(canvas.width, canvas.height);
|
311 |
-
for (i = 0; i < confettiPaperCount; i++) {
|
312 |
-
confettiPapers[i] = new ConfettiPaper(Math.random() * canvas.width, Math.random() * canvas.height);
|
313 |
-
}
|
314 |
-
this.resize = function() {
|
315 |
-
canvas.width = canvasParent.offsetWidth;
|
316 |
-
canvas.height = canvasParent.offsetHeight;
|
317 |
-
ConfettiPaper.bounds = new Vector2(canvas.width, canvas.height);
|
318 |
-
ConfettiRibbon.bounds = new Vector2(canvas.width, canvas.height);
|
319 |
-
}
|
320 |
-
this.start = function() {
|
321 |
-
this.stop()
|
322 |
-
var context = this
|
323 |
-
this.interval = setInterval(function() {
|
324 |
-
confetti.update();
|
325 |
-
}, 1000.0 / frameRate)
|
326 |
-
}
|
327 |
-
this.stop = function() {
|
328 |
-
clearInterval(this.interval);
|
329 |
-
}
|
330 |
-
this.update = function() {
|
331 |
-
var i = 0;
|
332 |
-
context.clearRect(0, 0, canvas.width, canvas.height);
|
333 |
-
for (i = 0; i < confettiPaperCount; i++) {
|
334 |
-
confettiPapers[i].Update(dt);
|
335 |
-
confettiPapers[i].Draw(context);
|
336 |
-
}
|
337 |
-
for (i = 0; i < confettiRibbonCount; i++) {
|
338 |
-
confettiRibbons[i].Update(dt);
|
339 |
-
confettiRibbons[i].Draw(context);
|
340 |
-
}
|
341 |
-
}
|
342 |
-
}
|
343 |
-
var confetti = new confetti.Context('confetti');
|
344 |
-
confetti.start();
|
345 |
-
$(window).resize(function() {
|
346 |
-
confetti.resize();
|
347 |
-
});
|
348 |
});
|
1 |
+
jQuery(document).ready(function($) {
|
2 |
+
var frameRate = 30;
|
3 |
+
var dt = 1.0 / frameRate;
|
4 |
+
var DEG_TO_RAD = Math.PI / 180;
|
5 |
+
var RAD_TO_DEG = 180 / Math.PI;
|
6 |
+
var colors = [
|
7 |
+
["#df0049", "#660671"],
|
8 |
+
["#00e857", "#005291"],
|
9 |
+
["#2bebbc", "#05798a"],
|
10 |
+
["#ffd200", "#b06c00"]
|
11 |
+
];
|
12 |
+
|
13 |
+
function Vector2(_x, _y) {
|
14 |
+
this.x = _x, this.y = _y;
|
15 |
+
this.Length = function() {
|
16 |
+
return Math.sqrt(this.SqrLength());
|
17 |
+
}
|
18 |
+
this.SqrLength = function() {
|
19 |
+
return this.x * this.x + this.y * this.y;
|
20 |
+
}
|
21 |
+
this.Equals = function(_vec0, _vec1) {
|
22 |
+
return _vec0.x == _vec1.x && _vec0.y == _vec1.y;
|
23 |
+
}
|
24 |
+
this.Add = function(_vec) {
|
25 |
+
this.x += _vec.x;
|
26 |
+
this.y += _vec.y;
|
27 |
+
}
|
28 |
+
this.Sub = function(_vec) {
|
29 |
+
this.x -= _vec.x;
|
30 |
+
this.y -= _vec.y;
|
31 |
+
}
|
32 |
+
this.Div = function(_f) {
|
33 |
+
this.x /= _f;
|
34 |
+
this.y /= _f;
|
35 |
+
}
|
36 |
+
this.Mul = function(_f) {
|
37 |
+
this.x *= _f;
|
38 |
+
this.y *= _f;
|
39 |
+
}
|
40 |
+
this.Normalize = function() {
|
41 |
+
var sqrLen = this.SqrLength();
|
42 |
+
if (sqrLen != 0) {
|
43 |
+
var factor = 1.0 / Math.sqrt(sqrLen);
|
44 |
+
this.x *= factor;
|
45 |
+
this.y *= factor;
|
46 |
+
}
|
47 |
+
}
|
48 |
+
this.Normalized = function() {
|
49 |
+
var sqrLen = this.SqrLength();
|
50 |
+
if (sqrLen != 0) {
|
51 |
+
var factor = 1.0 / Math.sqrt(sqrLen);
|
52 |
+
return new Vector2(this.x * factor, this.y * factor);
|
53 |
+
}
|
54 |
+
return new Vector2(0, 0);
|
55 |
+
}
|
56 |
+
}
|
57 |
+
Vector2.Lerp = function(_vec0, _vec1, _t) {
|
58 |
+
return new Vector2((_vec1.x - _vec0.x) * _t + _vec0.x, (_vec1.y - _vec0.y) * _t + _vec0.y);
|
59 |
+
}
|
60 |
+
Vector2.Distance = function(_vec0, _vec1) {
|
61 |
+
return Math.sqrt(Vector2.SqrDistance(_vec0, _vec1));
|
62 |
+
}
|
63 |
+
Vector2.SqrDistance = function(_vec0, _vec1) {
|
64 |
+
var x = _vec0.x - _vec1.x;
|
65 |
+
var y = _vec0.y - _vec1.y;
|
66 |
+
return (x * x + y * y + z * z);
|
67 |
+
}
|
68 |
+
Vector2.Scale = function(_vec0, _vec1) {
|
69 |
+
return new Vector2(_vec0.x * _vec1.x, _vec0.y * _vec1.y);
|
70 |
+
}
|
71 |
+
Vector2.Min = function(_vec0, _vec1) {
|
72 |
+
return new Vector2(Math.min(_vec0.x, _vec1.x), Math.min(_vec0.y, _vec1.y));
|
73 |
+
}
|
74 |
+
Vector2.Max = function(_vec0, _vec1) {
|
75 |
+
return new Vector2(Math.max(_vec0.x, _vec1.x), Math.max(_vec0.y, _vec1.y));
|
76 |
+
}
|
77 |
+
Vector2.ClampMagnitude = function(_vec0, _len) {
|
78 |
+
var vecNorm = _vec0.Normalized;
|
79 |
+
return new Vector2(vecNorm.x * _len, vecNorm.y * _len);
|
80 |
+
}
|
81 |
+
Vector2.Sub = function(_vec0, _vec1) {
|
82 |
+
return new Vector2(_vec0.x - _vec1.x, _vec0.y - _vec1.y, _vec0.z - _vec1.z);
|
83 |
+
}
|
84 |
+
|
85 |
+
function EulerMass(_x, _y, _mass, _drag) {
|
86 |
+
this.position = new Vector2(_x, _y);
|
87 |
+
this.mass = _mass;
|
88 |
+
this.drag = _drag;
|
89 |
+
this.force = new Vector2(0, 0);
|
90 |
+
this.velocity = new Vector2(0, 0);
|
91 |
+
this.AddForce = function(_f) {
|
92 |
+
this.force.Add(_f);
|
93 |
+
}
|
94 |
+
this.Integrate = function(_dt) {
|
95 |
+
var acc = this.CurrentForce(this.position);
|
96 |
+
acc.Div(this.mass);
|
97 |
+
var posDelta = new Vector2(this.velocity.x, this.velocity.y);
|
98 |
+
posDelta.Mul(_dt);
|
99 |
+
this.position.Add(posDelta);
|
100 |
+
acc.Mul(_dt);
|
101 |
+
this.velocity.Add(acc);
|
102 |
+
this.force = new Vector2(0, 0);
|
103 |
+
}
|
104 |
+
this.CurrentForce = function(_pos, _vel) {
|
105 |
+
var totalForce = new Vector2(this.force.x, this.force.y);
|
106 |
+
var speed = this.velocity.Length();
|
107 |
+
var dragVel = new Vector2(this.velocity.x, this.velocity.y);
|
108 |
+
dragVel.Mul(this.drag * this.mass * speed);
|
109 |
+
totalForce.Sub(dragVel);
|
110 |
+
return totalForce;
|
111 |
+
}
|
112 |
+
}
|
113 |
+
|
114 |
+
function ConfettiPaper(_x, _y) {
|
115 |
+
this.pos = new Vector2(_x, _y);
|
116 |
+
this.rotationSpeed = Math.random() * 600 + 800;
|
117 |
+
this.angle = DEG_TO_RAD * Math.random() * 360;
|
118 |
+
this.rotation = DEG_TO_RAD * Math.random() * 360;
|
119 |
+
this.cosA = 1.0;
|
120 |
+
this.size = 5.0;
|
121 |
+
this.oscillationSpeed = Math.random() * 1.5 + 0.5;
|
122 |
+
this.xSpeed = 40.0;
|
123 |
+
this.ySpeed = Math.random() * 60 + 50.0;
|
124 |
+
this.corners = new Array();
|
125 |
+
this.time = Math.random();
|
126 |
+
var ci = Math.round(Math.random() * (colors.length - 1));
|
127 |
+
this.frontColor = colors[ci][0];
|
128 |
+
this.backColor = colors[ci][1];
|
129 |
+
for (var i = 0; i < 4; i++) {
|
130 |
+
var dx = Math.cos(this.angle + DEG_TO_RAD * (i * 90 + 45));
|
131 |
+
var dy = Math.sin(this.angle + DEG_TO_RAD * (i * 90 + 45));
|
132 |
+
this.corners[i] = new Vector2(dx, dy);
|
133 |
+
}
|
134 |
+
this.Update = function(_dt) {
|
135 |
+
this.time += _dt;
|
136 |
+
this.rotation += this.rotationSpeed * _dt;
|
137 |
+
this.cosA = Math.cos(DEG_TO_RAD * this.rotation);
|
138 |
+
this.pos.x += Math.cos(this.time * this.oscillationSpeed) * this.xSpeed * _dt
|
139 |
+
this.pos.y += this.ySpeed * _dt;
|
140 |
+
if (this.pos.y > ConfettiPaper.bounds.y) {
|
141 |
+
this.pos.x = Math.random() * ConfettiPaper.bounds.x;
|
142 |
+
this.pos.y = 0;
|
143 |
+
}
|
144 |
+
}
|
145 |
+
this.Draw = function(_g) {
|
146 |
+
if (this.cosA > 0) {
|
147 |
+
_g.fillStyle = this.frontColor;
|
148 |
+
} else {
|
149 |
+
_g.fillStyle = this.backColor;
|
150 |
+
}
|
151 |
+
_g.beginPath();
|
152 |
+
_g.moveTo(this.pos.x + this.corners[0].x * this.size, this.pos.y + this.corners[0].y * this.size * this.cosA);
|
153 |
+
for (var i = 1; i < 4; i++) {
|
154 |
+
_g.lineTo(this.pos.x + this.corners[i].x * this.size, this.pos.y + this.corners[i].y * this.size * this.cosA);
|
155 |
+
}
|
156 |
+
_g.closePath();
|
157 |
+
_g.fill();
|
158 |
+
}
|
159 |
+
}
|
160 |
+
ConfettiPaper.bounds = new Vector2(0, 0);
|
161 |
+
|
162 |
+
function ConfettiRibbon(_x, _y, _count, _dist, _thickness, _angle, _mass, _drag) {
|
163 |
+
this.particleDist = _dist;
|
164 |
+
this.particleCount = _count;
|
165 |
+
this.particleMass = _mass;
|
166 |
+
this.particleDrag = _drag;
|
167 |
+
this.particles = new Array();
|
168 |
+
var ci = Math.round(Math.random() * (colors.length - 1));
|
169 |
+
this.frontColor = colors[ci][0];
|
170 |
+
this.backColor = colors[ci][1];
|
171 |
+
this.xOff = Math.cos(DEG_TO_RAD * _angle) * _thickness;
|
172 |
+
this.yOff = Math.sin(DEG_TO_RAD * _angle) * _thickness;
|
173 |
+
this.position = new Vector2(_x, _y);
|
174 |
+
this.prevPosition = new Vector2(_x, _y);
|
175 |
+
this.velocityInherit = Math.random() * 2 + 4;
|
176 |
+
this.time = Math.random() * 100;
|
177 |
+
this.oscillationSpeed = Math.random() * 2 + 2;
|
178 |
+
this.oscillationDistance = Math.random() * 40 + 40;
|
179 |
+
this.ySpeed = Math.random() * 40 + 80;
|
180 |
+
for (var i = 0; i < this.particleCount; i++) {
|
181 |
+
this.particles[i] = new EulerMass(_x, _y - i * this.particleDist, this.particleMass, this.particleDrag);
|
182 |
+
}
|
183 |
+
this.Update = function(_dt) {
|
184 |
+
var i = 0;
|
185 |
+
this.time += _dt * this.oscillationSpeed;
|
186 |
+
this.position.y += this.ySpeed * _dt;
|
187 |
+
this.position.x += Math.cos(this.time) * this.oscillationDistance * _dt;
|
188 |
+
this.particles[0].position = this.position;
|
189 |
+
var dX = this.prevPosition.x - this.position.x;
|
190 |
+
var dY = this.prevPosition.y - this.position.y;
|
191 |
+
var delta = Math.sqrt(dX * dX + dY * dY);
|
192 |
+
this.prevPosition = new Vector2(this.position.x, this.position.y);
|
193 |
+
for (i = 1; i < this.particleCount; i++) {
|
194 |
+
var dirP = Vector2.Sub(this.particles[i - 1].position, this.particles[i].position);
|
195 |
+
dirP.Normalize();
|
196 |
+
dirP.Mul((delta / _dt) * this.velocityInherit);
|
197 |
+
this.particles[i].AddForce(dirP);
|
198 |
+
}
|
199 |
+
for (i = 1; i < this.particleCount; i++) {
|
200 |
+
this.particles[i].Integrate(_dt);
|
201 |
+
}
|
202 |
+
for (i = 1; i < this.particleCount; i++) {
|
203 |
+
var rp2 = new Vector2(this.particles[i].position.x, this.particles[i].position.y);
|
204 |
+
rp2.Sub(this.particles[i - 1].position);
|
205 |
+
rp2.Normalize();
|
206 |
+
rp2.Mul(this.particleDist);
|
207 |
+
rp2.Add(this.particles[i - 1].position);
|
208 |
+
this.particles[i].position = rp2;
|
209 |
+
}
|
210 |
+
if (this.position.y > ConfettiRibbon.bounds.y + this.particleDist * this.particleCount) {
|
211 |
+
this.Reset();
|
212 |
+
}
|
213 |
+
}
|
214 |
+
this.Reset = function() {
|
215 |
+
this.position.y = -Math.random() * ConfettiRibbon.bounds.y;
|
216 |
+
this.position.x = Math.random() * ConfettiRibbon.bounds.x;
|
217 |
+
this.prevPosition = new Vector2(this.position.x, this.position.y);
|
218 |
+
this.velocityInherit = Math.random() * 2 + 4;
|
219 |
+
this.time = Math.random() * 100;
|
220 |
+
this.oscillationSpeed = Math.random() * 2.0 + 1.5;
|
221 |
+
this.oscillationDistance = Math.random() * 40 + 40;
|
222 |
+
this.ySpeed = Math.random() * 40 + 80;
|
223 |
+
var ci = Math.round(Math.random() * (colors.length - 1));
|
224 |
+
this.frontColor = colors[ci][0];
|
225 |
+
this.backColor = colors[ci][1];
|
226 |
+
this.particles = new Array();
|
227 |
+
for (var i = 0; i < this.particleCount; i++) {
|
228 |
+
this.particles[i] = new EulerMass(this.position.x, this.position.y - i * this.particleDist, this.particleMass, this.particleDrag);
|
229 |
+
}
|
230 |
+
}
|
231 |
+
this.Draw = function(_g) {
|
232 |
+
for (var i = 0; i < this.particleCount - 1; i++) {
|
233 |
+
var p0 = new Vector2(this.particles[i].position.x + this.xOff, this.particles[i].position.y + this.yOff);
|
234 |
+
var p1 = new Vector2(this.particles[i + 1].position.x + this.xOff, this.particles[i + 1].position.y + this.yOff);
|
235 |
+
if (this.Side(this.particles[i].position.x, this.particles[i].position.y, this.particles[i + 1].position.x, this.particles[i + 1].position.y, p1.x, p1.y) < 0) {
|
236 |
+
_g.fillStyle = this.frontColor;
|
237 |
+
_g.strokeStyle = this.frontColor;
|
238 |
+
} else {
|
239 |
+
_g.fillStyle = this.backColor;
|
240 |
+
_g.strokeStyle = this.backColor;
|
241 |
+
}
|
242 |
+
if (i == 0) {
|
243 |
+
_g.beginPath();
|
244 |
+
_g.moveTo(this.particles[i].position.x, this.particles[i].position.y);
|
245 |
+
_g.lineTo(this.particles[i + 1].position.x, this.particles[i + 1].position.y);
|
246 |
+
_g.lineTo((this.particles[i + 1].position.x + p1.x) * 0.5, (this.particles[i + 1].position.y + p1.y) * 0.5);
|
247 |
+
_g.closePath();
|
248 |
+
_g.stroke();
|
249 |
+
_g.fill();
|
250 |
+
_g.beginPath();
|
251 |
+
_g.moveTo(p1.x, p1.y);
|
252 |
+
_g.lineTo(p0.x, p0.y);
|
253 |
+
_g.lineTo((this.particles[i + 1].position.x + p1.x) * 0.5, (this.particles[i + 1].position.y + p1.y) * 0.5);
|
254 |
+
_g.closePath();
|
255 |
+
_g.stroke();
|
256 |
+
_g.fill();
|
257 |
+
} else if (i == this.particleCount - 2) {
|
258 |
+
_g.beginPath();
|
259 |
+
_g.moveTo(this.particles[i].position.x, this.particles[i].position.y);
|
260 |
+
_g.lineTo(this.particles[i + 1].position.x, this.particles[i + 1].position.y);
|
261 |
+
_g.lineTo((this.particles[i].position.x + p0.x) * 0.5, (this.particles[i].position.y + p0.y) * 0.5);
|
262 |
+
_g.closePath();
|
263 |
+
_g.stroke();
|
264 |
+
_g.fill();
|
265 |
+
_g.beginPath();
|
266 |
+
_g.moveTo(p1.x, p1.y);
|
267 |
+
_g.lineTo(p0.x, p0.y);
|
268 |
+
_g.lineTo((this.particles[i].position.x + p0.x) * 0.5, (this.particles[i].position.y + p0.y) * 0.5);
|
269 |
+
_g.closePath();
|
270 |
+
_g.stroke();
|
271 |
+
_g.fill();
|
272 |
+
} else {
|
273 |
+
_g.beginPath();
|
274 |
+
_g.moveTo(this.particles[i].position.x, this.particles[i].position.y);
|
275 |
+
_g.lineTo(this.particles[i + 1].position.x, this.particles[i + 1].position.y);
|
276 |
+
_g.lineTo(p1.x, p1.y);
|
277 |
+
_g.lineTo(p0.x, p0.y);
|
278 |
+
_g.closePath();
|
279 |
+
_g.stroke();
|
280 |
+
_g.fill();
|
281 |
+
}
|
282 |
+
}
|
283 |
+
}
|
284 |
+
this.Side = function(x1, y1, x2, y2, x3, y3) {
|
285 |
+
return ((x1 - x2) * (y3 - y2) - (y1 - y2) * (x3 - x2));
|
286 |
+
}
|
287 |
+
}
|
288 |
+
ConfettiRibbon.bounds = new Vector2(0, 0);
|
289 |
+
confetti = {};
|
290 |
+
confetti.Context = function(parent) {
|
291 |
+
var i = 0;
|
292 |
+
var canvasParent = document.getElementById(parent);
|
293 |
+
var canvas = document.createElement('canvas');
|
294 |
+
canvas.width = canvasParent.offsetWidth;
|
295 |
+
canvas.height = canvasParent.offsetHeight;
|
296 |
+
canvasParent.appendChild(canvas);
|
297 |
+
var context = canvas.getContext('2d');
|
298 |
+
var interval = null;
|
299 |
+
var confettiRibbonCount = 7;
|
300 |
+
var rpCount = 30;
|
301 |
+
var rpDist = 8.0;
|
302 |
+
var rpThick = 8.0;
|
303 |
+
var confettiRibbons = new Array();
|
304 |
+
ConfettiRibbon.bounds = new Vector2(canvas.width, canvas.height);
|
305 |
+
for (i = 0; i < confettiRibbonCount; i++) {
|
306 |
+
confettiRibbons[i] = new ConfettiRibbon(Math.random() * canvas.width, -Math.random() * canvas.height * 2, rpCount, rpDist, rpThick, 45, 1, 0.05);
|
307 |
+
}
|
308 |
+
var confettiPaperCount = 25;
|
309 |
+
var confettiPapers = new Array();
|
310 |
+
ConfettiPaper.bounds = new Vector2(canvas.width, canvas.height);
|
311 |
+
for (i = 0; i < confettiPaperCount; i++) {
|
312 |
+
confettiPapers[i] = new ConfettiPaper(Math.random() * canvas.width, Math.random() * canvas.height);
|
313 |
+
}
|
314 |
+
this.resize = function() {
|
315 |
+
canvas.width = canvasParent.offsetWidth;
|
316 |
+
canvas.height = canvasParent.offsetHeight;
|
317 |
+
ConfettiPaper.bounds = new Vector2(canvas.width, canvas.height);
|
318 |
+
ConfettiRibbon.bounds = new Vector2(canvas.width, canvas.height);
|
319 |
+
}
|
320 |
+
this.start = function() {
|
321 |
+
this.stop()
|
322 |
+
var context = this
|
323 |
+
this.interval = setInterval(function() {
|
324 |
+
confetti.update();
|
325 |
+
}, 1000.0 / frameRate)
|
326 |
+
}
|
327 |
+
this.stop = function() {
|
328 |
+
clearInterval(this.interval);
|
329 |
+
}
|
330 |
+
this.update = function() {
|
331 |
+
var i = 0;
|
332 |
+
context.clearRect(0, 0, canvas.width, canvas.height);
|
333 |
+
for (i = 0; i < confettiPaperCount; i++) {
|
334 |
+
confettiPapers[i].Update(dt);
|
335 |
+
confettiPapers[i].Draw(context);
|
336 |
+
}
|
337 |
+
for (i = 0; i < confettiRibbonCount; i++) {
|
338 |
+
confettiRibbons[i].Update(dt);
|
339 |
+
confettiRibbons[i].Draw(context);
|
340 |
+
}
|
341 |
+
}
|
342 |
+
}
|
343 |
+
var confetti = new confetti.Context('confetti');
|
344 |
+
confetti.start();
|
345 |
+
$(window).resize(function() {
|
346 |
+
confetti.resize();
|
347 |
+
});
|
348 |
});
|
assets/js/order-script.js
CHANGED
@@ -1,42 +1,42 @@
|
|
1 |
-
jQuery(document).ready(function($) {
|
2 |
-
$("#doaction, #doaction2").click(function (event) {
|
3 |
-
var actionselected = $(this).attr("id").substr(2);
|
4 |
-
var action = $('select[name="' + actionselected + '"]').val();
|
5 |
-
if ( $.inArray(action, wpo_wcpdf_ajax.bulk_actions) !== -1 ) {
|
6 |
-
event.preventDefault();
|
7 |
-
var template = action;
|
8 |
-
var checked = [];
|
9 |
-
$('tbody th.check-column input[type="checkbox"]:checked').each(
|
10 |
-
function() {
|
11 |
-
checked.push($(this).val());
|
12 |
-
}
|
13 |
-
);
|
14 |
-
|
15 |
-
if (!checked.length) {
|
16 |
-
alert('You have to select order(s) first!');
|
17 |
-
return;
|
18 |
-
}
|
19 |
-
|
20 |
-
var order_ids=checked.join('x');
|
21 |
-
|
22 |
-
if (wpo_wcpdf_ajax.ajaxurl.indexOf("?") != -1) {
|
23 |
-
url = wpo_wcpdf_ajax.ajaxurl+'&action=generate_wpo_wcpdf&document_type='+template+'&order_ids='+order_ids+'&_wpnonce='+wpo_wcpdf_ajax.nonce;
|
24 |
-
} else {
|
25 |
-
url = wpo_wcpdf_ajax.ajaxurl+'?action=generate_wpo_wcpdf&document_type='+template+'&order_ids='+order_ids+'&_wpnonce='+wpo_wcpdf_ajax.nonce;
|
26 |
-
}
|
27 |
-
|
28 |
-
window.open(url,'_blank');
|
29 |
-
}
|
30 |
-
});
|
31 |
-
|
32 |
-
$('#wpo_wcpdf-data-input-box').insertAfter('#woocommerce-order-data');
|
33 |
-
|
34 |
-
// enable invoice number edit if user initiated
|
35 |
-
$( ".wpo-wcpdf-set-date-number, .wpo-wcpdf-edit-date-number" ).click(function() {
|
36 |
-
$form = $(this).closest('.wcpdf-data-fields');
|
37 |
-
$form.find(".read-only").hide();
|
38 |
-
$form.find(".editable").show();
|
39 |
-
$form.find(':input').prop('disabled', false);
|
40 |
-
});
|
41 |
-
});
|
42 |
-
|
1 |
+
jQuery(document).ready(function($) {
|
2 |
+
$("#doaction, #doaction2").click(function (event) {
|
3 |
+
var actionselected = $(this).attr("id").substr(2);
|
4 |
+
var action = $('select[name="' + actionselected + '"]').val();
|
5 |
+
if ( $.inArray(action, wpo_wcpdf_ajax.bulk_actions) !== -1 ) {
|
6 |
+
event.preventDefault();
|
7 |
+
var template = action;
|
8 |
+
var checked = [];
|
9 |
+
$('tbody th.check-column input[type="checkbox"]:checked').each(
|
10 |
+
function() {
|
11 |
+
checked.push($(this).val());
|
12 |
+
}
|
13 |
+
);
|
14 |
+
|
15 |
+
if (!checked.length) {
|
16 |
+
alert('You have to select order(s) first!');
|
17 |
+
return;
|
18 |
+
}
|
19 |
+
|
20 |
+
var order_ids=checked.join('x');
|
21 |
+
|
22 |
+
if (wpo_wcpdf_ajax.ajaxurl.indexOf("?") != -1) {
|
23 |
+
url = wpo_wcpdf_ajax.ajaxurl+'&action=generate_wpo_wcpdf&document_type='+template+'&order_ids='+order_ids+'&_wpnonce='+wpo_wcpdf_ajax.nonce;
|
24 |
+
} else {
|
25 |
+
url = wpo_wcpdf_ajax.ajaxurl+'?action=generate_wpo_wcpdf&document_type='+template+'&order_ids='+order_ids+'&_wpnonce='+wpo_wcpdf_ajax.nonce;
|
26 |
+
}
|
27 |
+
|
28 |
+
window.open(url,'_blank');
|
29 |
+
}
|
30 |
+
});
|
31 |
+
|
32 |
+
$('#wpo_wcpdf-data-input-box').insertAfter('#woocommerce-order-data');
|
33 |
+
|
34 |
+
// enable invoice number edit if user initiated
|
35 |
+
$( ".wpo-wcpdf-set-date-number, .wpo-wcpdf-edit-date-number" ).click(function() {
|
36 |
+
$form = $(this).closest('.wcpdf-data-fields');
|
37 |
+
$form.find(".read-only").hide();
|
38 |
+
$form.find(".editable").show();
|
39 |
+
$form.find(':input').prop('disabled', false);
|
40 |
+
});
|
41 |
+
});
|
42 |
+
|
assets/js/setup-wizard.js
CHANGED
@@ -1,20 +1,20 @@
|
|
1 |
-
jQuery( function( $ ) {
|
2 |
-
|
3 |
-
$( '.tab' ).click(function() {
|
4 |
-
$( this ).closest('.extra-field').find('.tab').removeClass( 'active' );
|
5 |
-
$( this ).addClass( 'active' );
|
6 |
-
var $language = $( this ).attr('id');
|
7 |
-
console.log($language);
|
8 |
-
$( this ).siblings('.extra-field-input').hide();
|
9 |
-
$('.' + $language ).show();
|
10 |
-
});
|
11 |
-
|
12 |
-
// Show Preview of logo
|
13 |
-
$('#file-upload').change( function(event) {
|
14 |
-
if ( event.target.files[0] ) {
|
15 |
-
var tmppath = URL.createObjectURL(event.target.files[0]);
|
16 |
-
$( '#logo-preview' ).find( "img" ).attr( 'src',tmppath );
|
17 |
-
}
|
18 |
-
});
|
19 |
-
|
20 |
-
});
|
1 |
+
jQuery( function( $ ) {
|
2 |
+
|
3 |
+
$( '.tab' ).click(function() {
|
4 |
+
$( this ).closest('.extra-field').find('.tab').removeClass( 'active' );
|
5 |
+
$( this ).addClass( 'active' );
|
6 |
+
var $language = $( this ).attr('id');
|
7 |
+
console.log($language);
|
8 |
+
$( this ).siblings('.extra-field-input').hide();
|
9 |
+
$('.' + $language ).show();
|
10 |
+
});
|
11 |
+
|
12 |
+
// Show Preview of logo
|
13 |
+
$('#file-upload').change( function(event) {
|
14 |
+
if ( event.target.files[0] ) {
|
15 |
+
var tmppath = URL.createObjectURL(event.target.files[0]);
|
16 |
+
$( '#logo-preview' ).find( "img" ).attr( 'src',tmppath );
|
17 |
+
}
|
18 |
+
});
|
19 |
+
|
20 |
+
});
|
composer.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
-
{
|
2 |
-
"require": {
|
3 |
-
"php": ">=5.3.0",
|
4 |
-
"dompdf/dompdf": "*"
|
5 |
-
}
|
6 |
}
|
1 |
+
{
|
2 |
+
"require": {
|
3 |
+
"php": ">=5.3.0",
|
4 |
+
"dompdf/dompdf": "*"
|
5 |
+
}
|
6 |
}
|
composer.lock
CHANGED
@@ -1,203 +1,203 @@
|
|
1 |
-
{
|
2 |
-
"_readme": [
|
3 |
-
"This file locks the dependencies of your project to a known state",
|
4 |
-
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
5 |
-
"This file is @generated automatically"
|
6 |
-
],
|
7 |
-
"content-hash": "7e8e737c6fffa1c43aefc1018cab381f",
|
8 |
-
"packages": [
|
9 |
-
{
|
10 |
-
"name": "dompdf/dompdf",
|
11 |
-
"version": "v0.8.2",
|
12 |
-
"source": {
|
13 |
-
"type": "git",
|
14 |
-
"url": "https://github.com/dompdf/dompdf.git",
|
15 |
-
"reference": "5113accd9ae5d466077cce5208dcf3fb871bf8f6"
|
16 |
-
},
|
17 |
-
"dist": {
|
18 |
-
"type": "zip",
|
19 |
-
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/5113accd9ae5d466077cce5208dcf3fb871bf8f6",
|
20 |
-
"reference": "5113accd9ae5d466077cce5208dcf3fb871bf8f6",
|
21 |
-
"shasum": ""
|
22 |
-
},
|
23 |
-
"require": {
|
24 |
-
"ext-dom": "*",
|
25 |
-
"ext-gd": "*",
|
26 |
-
"ext-mbstring": "*",
|
27 |
-
"phenx/php-font-lib": "0.5.*",
|
28 |
-
"phenx/php-svg-lib": "0.3.*",
|
29 |
-
"php": ">=5.4.0"
|
30 |
-
},
|
31 |
-
"require-dev": {
|
32 |
-
"phpunit/phpunit": "4.8.*",
|
33 |
-
"squizlabs/php_codesniffer": "2.*"
|
34 |
-
},
|
35 |
-
"type": "library",
|
36 |
-
"extra": {
|
37 |
-
"branch-alias": {
|
38 |
-
"dev-develop": "0.7-dev"
|
39 |
-
}
|
40 |
-
},
|
41 |
-
"autoload": {
|
42 |
-
"psr-4": {
|
43 |
-
"Dompdf\\": "src/"
|
44 |
-
},
|
45 |
-
"classmap": [
|
46 |
-
"lib/"
|
47 |
-
]
|
48 |
-
},
|
49 |
-
"notification-url": "https://packagist.org/downloads/",
|
50 |
-
"license": [
|
51 |
-
"LGPL-2.1"
|
52 |
-
],
|
53 |
-
"authors": [
|
54 |
-
{
|
55 |
-
"name": "Fabien Ménager",
|
56 |
-
"email": "fabien.menager@gmail.com"
|
57 |
-
},
|
58 |
-
{
|
59 |
-
"name": "Brian Sweeney",
|
60 |
-
"email": "eclecticgeek@gmail.com"
|
61 |
-
},
|
62 |
-
{
|
63 |
-
"name": "Gabriel Bull",
|
64 |
-
"email": "me@gabrielbull.com"
|
65 |
-
}
|
66 |
-
],
|
67 |
-
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
|
68 |
-
"homepage": "https://github.com/dompdf/dompdf",
|
69 |
-
"time": "2017-11-26T14:49:08+00:00"
|
70 |
-
},
|
71 |
-
{
|
72 |
-
"name": "phenx/php-font-lib",
|
73 |
-
"version": "0.5.1",
|
74 |
-
"source": {
|
75 |
-
"type": "git",
|
76 |
-
"url": "https://github.com/PhenX/php-font-lib.git",
|
77 |
-
"reference": "760148820110a1ae0936e5cc35851e25a938bc97"
|
78 |
-
},
|
79 |
-
"dist": {
|
80 |
-
"type": "zip",
|
81 |
-
"url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/760148820110a1ae0936e5cc35851e25a938bc97",
|
82 |
-
"reference": "760148820110a1ae0936e5cc35851e25a938bc97",
|
83 |
-
"shasum": ""
|
84 |
-
},
|
85 |
-
"require-dev": {
|
86 |
-
"phpunit/phpunit": "^4.8"
|
87 |
-
},
|
88 |
-
"type": "library",
|
89 |
-
"autoload": {
|
90 |
-
"psr-4": {
|
91 |
-
"FontLib\\": "src/FontLib"
|
92 |
-
}
|
93 |
-
},
|
94 |
-
"notification-url": "https://packagist.org/downloads/",
|
95 |
-
"license": [
|
96 |
-
"LGPL-3.0"
|
97 |
-
],
|
98 |
-
"authors": [
|
99 |
-
{
|
100 |
-
"name": "Fabien Ménager",
|
101 |
-
"email": "fabien.menager@gmail.com"
|
102 |
-
}
|
103 |
-
],
|
104 |
-
"description": "A library to read, parse, export and make subsets of different types of font files.",
|
105 |
-
"homepage": "https://github.com/PhenX/php-font-lib",
|
106 |
-
"time": "2017-09-13T16:14:37+00:00"
|
107 |
-
},
|
108 |
-
{
|
109 |
-
"name": "phenx/php-svg-lib",
|
110 |
-
"version": "v0.3",
|
111 |
-
"source": {
|
112 |
-
"type": "git",
|
113 |
-
"url": "https://github.com/PhenX/php-svg-lib.git",
|
114 |
-
"reference": "a85f7fe9fe08d093a4a8583cdd306b553ff918aa"
|
115 |
-
},
|
116 |
-
"dist": {
|
117 |
-
"type": "zip",
|
118 |
-
"url": "https://api.github.com/repos/PhenX/php-svg-lib/zipball/a85f7fe9fe08d093a4a8583cdd306b553ff918aa",
|
119 |
-
"reference": "a85f7fe9fe08d093a4a8583cdd306b553ff918aa",
|
120 |
-
"shasum": ""
|
121 |
-
},
|
122 |
-
"require": {
|
123 |
-
"sabberworm/php-css-parser": "8.1.*"
|
124 |
-
},
|
125 |
-
"require-dev": {
|
126 |
-
"phpunit/phpunit": "~5.0"
|
127 |
-
},
|
128 |
-
"type": "library",
|
129 |
-
"autoload": {
|
130 |
-
"psr-0": {
|
131 |
-
"Svg\\": "src/"
|
132 |
-
}
|
133 |
-
},
|
134 |
-
"notification-url": "https://packagist.org/downloads/",
|
135 |
-
"license": [
|
136 |
-
"LGPL-3.0"
|
137 |
-
],
|
138 |
-
"authors": [
|
139 |
-
{
|
140 |
-
"name": "Fabien Ménager",
|
141 |
-
"email": "fabien.menager@gmail.com"
|
142 |
-
}
|
143 |
-
],
|
144 |
-
"description": "A library to read, parse and export to PDF SVG files.",
|
145 |
-
"homepage": "https://github.com/PhenX/php-svg-lib",
|
146 |
-
"time": "2017-05-24T10:07:27+00:00"
|
147 |
-
},
|
148 |
-
{
|
149 |
-
"name": "sabberworm/php-css-parser",
|
150 |
-
"version": "8.1.0",
|
151 |
-
"source": {
|
152 |
-
"type": "git",
|
153 |
-
"url": "https://github.com/sabberworm/PHP-CSS-Parser.git",
|
154 |
-
"reference": "850cbbcbe7fbb155387a151ea562897a67e242ef"
|
155 |
-
},
|
156 |
-
"dist": {
|
157 |
-
"type": "zip",
|
158 |
-
"url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/850cbbcbe7fbb155387a151ea562897a67e242ef",
|
159 |
-
"reference": "850cbbcbe7fbb155387a151ea562897a67e242ef",
|
160 |
-
"shasum": ""
|
161 |
-
},
|
162 |
-
"require": {
|
163 |
-
"php": ">=5.3.2"
|
164 |
-
},
|
165 |
-
"require-dev": {
|
166 |
-
"phpunit/phpunit": "*"
|
167 |
-
},
|
168 |
-
"type": "library",
|
169 |
-
"autoload": {
|
170 |
-
"psr-0": {
|
171 |
-
"Sabberworm\\CSS": "lib/"
|
172 |
-
}
|
173 |
-
},
|
174 |
-
"notification-url": "https://packagist.org/downloads/",
|
175 |
-
"license": [
|
176 |
-
"MIT"
|
177 |
-
],
|
178 |
-
"authors": [
|
179 |
-
{
|
180 |
-
"name": "Raphael Schweikert"
|
181 |
-
}
|
182 |
-
],
|
183 |
-
"description": "Parser for CSS Files written in PHP",
|
184 |
-
"homepage": "http://www.sabberworm.com/blog/2010/6/10/php-css-parser",
|
185 |
-
"keywords": [
|
186 |
-
"css",
|
187 |
-
"parser",
|
188 |
-
"stylesheet"
|
189 |
-
],
|
190 |
-
"time": "2016-07-19T19:14:21+00:00"
|
191 |
-
}
|
192 |
-
],
|
193 |
-
"packages-dev": [],
|
194 |
-
"aliases": [],
|
195 |
-
"minimum-stability": "stable",
|
196 |
-
"stability-flags": [],
|
197 |
-
"prefer-stable": false,
|
198 |
-
"prefer-lowest": false,
|
199 |
-
"platform": {
|
200 |
-
"php": ">=5.3.0"
|
201 |
-
},
|
202 |
-
"platform-dev": []
|
203 |
-
}
|
1 |
+
{
|
2 |
+
"_readme": [
|
3 |
+
"This file locks the dependencies of your project to a known state",
|
4 |
+
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
5 |
+
"This file is @generated automatically"
|
6 |
+
],
|
7 |
+
"content-hash": "7e8e737c6fffa1c43aefc1018cab381f",
|
8 |
+
"packages": [
|
9 |
+
{
|
10 |
+
"name": "dompdf/dompdf",
|
11 |
+
"version": "v0.8.2",
|
12 |
+
"source": {
|
13 |
+
"type": "git",
|
14 |
+
"url": "https://github.com/dompdf/dompdf.git",
|
15 |
+
"reference": "5113accd9ae5d466077cce5208dcf3fb871bf8f6"
|
16 |
+
},
|
17 |
+
"dist": {
|
18 |
+
"type": "zip",
|
19 |
+
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/5113accd9ae5d466077cce5208dcf3fb871bf8f6",
|
20 |
+
"reference": "5113accd9ae5d466077cce5208dcf3fb871bf8f6",
|
21 |
+
"shasum": ""
|
22 |
+
},
|
23 |
+
"require": {
|
24 |
+
"ext-dom": "*",
|
25 |
+
"ext-gd": "*",
|
26 |
+
"ext-mbstring": "*",
|
27 |
+
"phenx/php-font-lib": "0.5.*",
|
28 |
+
"phenx/php-svg-lib": "0.3.*",
|
29 |
+
"php": ">=5.4.0"
|
30 |
+
},
|
31 |
+
"require-dev": {
|
32 |
+
"phpunit/phpunit": "4.8.*",
|
33 |
+
"squizlabs/php_codesniffer": "2.*"
|
34 |
+
},
|
35 |
+
"type": "library",
|
36 |
+
"extra": {
|
37 |
+
"branch-alias": {
|
38 |
+
"dev-develop": "0.7-dev"
|
39 |
+
}
|
40 |
+
},
|
41 |
+
"autoload": {
|
42 |
+
"psr-4": {
|
43 |
+
"Dompdf\\": "src/"
|
44 |
+
},
|
45 |
+
"classmap": [
|
46 |
+
"lib/"
|
47 |
+
]
|
48 |
+
},
|
49 |
+
"notification-url": "https://packagist.org/downloads/",
|
50 |
+
"license": [
|
51 |
+
"LGPL-2.1"
|
52 |
+
],
|
53 |
+
"authors": [
|
54 |
+
{
|
55 |
+
"name": "Fabien Ménager",
|
56 |
+
"email": "fabien.menager@gmail.com"
|
57 |
+
},
|
58 |
+
{
|
59 |
+
"name": "Brian Sweeney",
|
60 |
+
"email": "eclecticgeek@gmail.com"
|
61 |
+
},
|
62 |
+
{
|
63 |
+
"name": "Gabriel Bull",
|
64 |
+
"email": "me@gabrielbull.com"
|
65 |
+
}
|
66 |
+
],
|
67 |
+
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
|
68 |
+
"homepage": "https://github.com/dompdf/dompdf",
|
69 |
+
"time": "2017-11-26T14:49:08+00:00"
|
70 |
+
},
|
71 |
+
{
|
72 |
+
"name": "phenx/php-font-lib",
|
73 |
+
"version": "0.5.1",
|
74 |
+
"source": {
|
75 |
+
"type": "git",
|
76 |
+
"url": "https://github.com/PhenX/php-font-lib.git",
|
77 |
+
"reference": "760148820110a1ae0936e5cc35851e25a938bc97"
|
78 |
+
},
|
79 |
+
"dist": {
|
80 |
+
"type": "zip",
|
81 |
+
"url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/760148820110a1ae0936e5cc35851e25a938bc97",
|
82 |
+
"reference": "760148820110a1ae0936e5cc35851e25a938bc97",
|
83 |
+
"shasum": ""
|
84 |
+
},
|
85 |
+
"require-dev": {
|
86 |
+
"phpunit/phpunit": "^4.8"
|
87 |
+
},
|
88 |
+
"type": "library",
|
89 |
+
"autoload": {
|
90 |
+
"psr-4": {
|
91 |
+
"FontLib\\": "src/FontLib"
|
92 |
+
}
|
93 |
+
},
|
94 |
+
"notification-url": "https://packagist.org/downloads/",
|
95 |
+
"license": [
|
96 |
+
"LGPL-3.0"
|
97 |
+
],
|
98 |
+
"authors": [
|
99 |
+
{
|
100 |
+
"name": "Fabien Ménager",
|
101 |
+
"email": "fabien.menager@gmail.com"
|
102 |
+
}
|
103 |
+
],
|
104 |
+
"description": "A library to read, parse, export and make subsets of different types of font files.",
|
105 |
+
"homepage": "https://github.com/PhenX/php-font-lib",
|
106 |
+
"time": "2017-09-13T16:14:37+00:00"
|
107 |
+
},
|
108 |
+
{
|
109 |
+
"name": "phenx/php-svg-lib",
|
110 |
+
"version": "v0.3",
|
111 |
+
"source": {
|
112 |
+
"type": "git",
|
113 |
+
"url": "https://github.com/PhenX/php-svg-lib.git",
|
114 |
+
"reference": "a85f7fe9fe08d093a4a8583cdd306b553ff918aa"
|
115 |
+
},
|
116 |
+
"dist": {
|
117 |
+
"type": "zip",
|
118 |
+
"url": "https://api.github.com/repos/PhenX/php-svg-lib/zipball/a85f7fe9fe08d093a4a8583cdd306b553ff918aa",
|
119 |
+
"reference": "a85f7fe9fe08d093a4a8583cdd306b553ff918aa",
|
120 |
+
"shasum": ""
|
121 |
+
},
|
122 |
+
"require": {
|
123 |
+
"sabberworm/php-css-parser": "8.1.*"
|
124 |
+
},
|
125 |
+
"require-dev": {
|
126 |
+
"phpunit/phpunit": "~5.0"
|
127 |
+
},
|
128 |
+
"type": "library",
|
129 |
+
"autoload": {
|
130 |
+
"psr-0": {
|
131 |
+
"Svg\\": "src/"
|
132 |
+
}
|
133 |
+
},
|
134 |
+
"notification-url": "https://packagist.org/downloads/",
|
135 |
+
"license": [
|
136 |
+
"LGPL-3.0"
|
137 |
+
],
|
138 |
+
"authors": [
|
139 |
+
{
|
140 |
+
"name": "Fabien Ménager",
|
141 |
+
"email": "fabien.menager@gmail.com"
|
142 |
+
}
|
143 |
+
],
|
144 |
+
"description": "A library to read, parse and export to PDF SVG files.",
|
145 |
+
"homepage": "https://github.com/PhenX/php-svg-lib",
|
146 |
+
"time": "2017-05-24T10:07:27+00:00"
|
147 |
+
},
|
148 |
+
{
|
149 |
+
"name": "sabberworm/php-css-parser",
|
150 |
+
"version": "8.1.0",
|
151 |
+
"source": {
|
152 |
+
"type": "git",
|
153 |
+
"url": "https://github.com/sabberworm/PHP-CSS-Parser.git",
|
154 |
+
"reference": "850cbbcbe7fbb155387a151ea562897a67e242ef"
|
155 |
+
},
|
156 |
+
"dist": {
|
157 |
+
"type": "zip",
|
158 |
+
"url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/850cbbcbe7fbb155387a151ea562897a67e242ef",
|
159 |
+
"reference": "850cbbcbe7fbb155387a151ea562897a67e242ef",
|
160 |
+
"shasum": ""
|
161 |
+
},
|
162 |
+
"require": {
|
163 |
+
"php": ">=5.3.2"
|
164 |
+
},
|
165 |
+
"require-dev": {
|
166 |
+
"phpunit/phpunit": "*"
|
167 |
+
},
|
168 |
+
"type": "library",
|
169 |
+
"autoload": {
|
170 |
+
"psr-0": {
|
171 |
+
"Sabberworm\\CSS": "lib/"
|
172 |
+
}
|
173 |
+
},
|
174 |
+
"notification-url": "https://packagist.org/downloads/",
|
175 |
+
"license": [
|
176 |
+
"MIT"
|
177 |
+
],
|
178 |
+
"authors": [
|
179 |
+
{
|
180 |
+
"name": "Raphael Schweikert"
|
181 |
+
}
|
182 |
+
],
|
183 |
+
"description": "Parser for CSS Files written in PHP",
|
184 |
+
"homepage": "http://www.sabberworm.com/blog/2010/6/10/php-css-parser",
|
185 |
+
"keywords": [
|
186 |
+
"css",
|
187 |
+
"parser",
|
188 |
+
"stylesheet"
|
189 |
+
],
|
190 |
+
"time": "2016-07-19T19:14:21+00:00"
|
191 |
+
}
|
192 |
+
],
|
193 |
+
"packages-dev": [],
|
194 |
+
"aliases": [],
|
195 |
+
"minimum-stability": "stable",
|
196 |
+
"stability-flags": [],
|
197 |
+
"prefer-stable": false,
|
198 |
+
"prefer-lowest": false,
|
199 |
+
"platform": {
|
200 |
+
"php": ">=5.3.0"
|
201 |
+
},
|
202 |
+
"platform-dev": []
|
203 |
+
}
|
includes/class-wcpdf-admin.php
CHANGED
@@ -1,489 +1,489 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPO\WC\PDF_Invoices;
|
3 |
-
|
4 |
-
use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
|
5 |
-
use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
|
6 |
-
use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
|
7 |
-
|
8 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
9 |
-
exit; // Exit if accessed directly
|
10 |
-
}
|
11 |
-
|
12 |
-
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Admin' ) ) :
|
13 |
-
|
14 |
-
class Admin {
|
15 |
-
function __construct() {
|
16 |
-
add_action( 'woocommerce_admin_order_actions_end', array( $this, 'add_listing_actions' ) );
|
17 |
-
add_filter( 'manage_edit-shop_order_columns', array( $this, 'add_invoice_number_column' ), 999 );
|
18 |
-
add_action( 'manage_shop_order_posts_custom_column', array( $this, 'invoice_number_column_data' ), 2 );
|
19 |
-
add_action( 'add_meta_boxes_shop_order', array( $this, 'add_meta_boxes' ) );
|
20 |
-
add_action( 'admin_footer', array( $this, 'bulk_actions' ) );
|
21 |
-
add_filter( 'woocommerce_shop_order_search_fields', array( $this, 'search_fields' ) );
|
22 |
-
|
23 |
-
add_action( 'save_post', array( $this,'save_invoice_number_date' ) );
|
24 |
-
|
25 |
-
// manually send emails
|
26 |
-
// WooCommerce core processes order actions at priority 50
|
27 |
-
add_action( 'woocommerce_process_shop_order_meta', array( $this, 'send_emails' ), 60, 2 );
|
28 |
-
|
29 |
-
add_action( 'admin_notices', array( $this, 'review_plugin_notice' ) );
|
30 |
-
|
31 |
-
add_action( 'init', array( $this, 'setup_wizard') );
|
32 |
-
// add_action( 'wpo_wcpdf_after_pdf', array( $this,'update_pdf_counter' ), 10, 2 );
|
33 |
-
|
34 |
-
add_filter( 'manage_edit-shop_order_sortable_columns', array( $this, 'invoice_number_column_sortable' ) );
|
35 |
-
add_filter( 'pre_get_posts', array( $this, 'sort_by_invoice_number' ) );
|
36 |
-
}
|
37 |
-
|
38 |
-
// display review admin notice after 100 pdf downloads
|
39 |
-
public function review_plugin_notice() {
|
40 |
-
if ( $this->is_order_page() === false && !( isset( $_GET['page'] ) && $_GET['page'] == 'wpo_wcpdf_options_page' ) ) {
|
41 |
-
return;
|
42 |
-
}
|
43 |
-
|
44 |
-
if ( get_option( 'wpo_wcpdf_review_notice_dismissed' ) !== false ) {
|
45 |
-
return;
|
46 |
-
} else {
|
47 |
-
if ( isset( $_GET['wpo_wcpdf_dismis_review'] ) ) {
|
48 |
-
update_option( 'wpo_wcpdf_review_notice_dismissed', true );
|
49 |
-
return;
|
50 |
-
}
|
51 |
-
|
52 |
-
// keep track of how many days this notice is show so we can remove it after 7 days
|
53 |
-
$notice_shown_on = get_option( 'wpo_wcpdf_review_notice_shown', array() );
|
54 |
-
$today = date('Y-m-d');
|
55 |
-
if ( !in_array($today, $notice_shown_on) ) {
|
56 |
-
$notice_shown_on[] = $today;
|
57 |
-
update_option( 'wpo_wcpdf_review_notice_shown', $notice_shown_on );
|
58 |
-
}
|
59 |
-
// count number of days review is shown, dismiss forever if shown more than 7
|
60 |
-
if (count($notice_shown_on) > 7) {
|
61 |
-
update_option( 'wpo_wcpdf_review_notice_dismissed', true );
|
62 |
-
return;
|
63 |
-
}
|
64 |
-
|
65 |
-
// get invoice count to determine whether notice should be shown
|
66 |
-
$invoice_count = $this->get_invoice_count();
|
67 |
-
if ( $invoice_count > 100 ) {
|
68 |
-
$rounded_count = (int) substr( (string) $invoice_count, 0, 1 ) * pow( 10, strlen( (string) $invoice_count ) - 1);
|
69 |
-
?>
|
70 |
-
<div class="notice notice-info is-dismissible wpo-wcpdf-review-notice">
|
71 |
-
<h3><?php printf( __( 'Wow, you have created more than %d invoices with our plugin!', 'woocommerce-pdf-invoices-packing-slips' ), $rounded_count ); ?></h3>
|
72 |
-
<p><?php _e( 'It would mean a lot to us if you would quickly give our plugin a 5-star rating. Help us spread the word and boost our motivation!', 'woocommerce-pdf-invoices-packing-slips' ); ?></p>
|
73 |
-
<ul>
|
74 |
-
<li><a href="https://wordpress.org/support/plugin/woocommerce-pdf-invoices-packing-slips/reviews/?rate=5#new-post" class="button"><?php _e( 'Yes you deserve it!', 'woocommerce-pdf-invoices-packing-slips' ); ?></span></a></li>
|
75 |
-
<li><a href="<?php echo esc_url( add_query_arg( 'wpo_wcpdf_dismis_review', true ) ); ?>" class="wpo-wcpdf-dismiss"><?php _e( 'Hide this message', 'woocommerce-pdf-invoices-packing-slips' ); ?> / <?php _e( 'Already did!', 'woocommerce-pdf-invoices-packing-slips' ); ?></a></li>
|
76 |
-
<li><a href="mailto:support@wpovernight.com?Subject=Here%20is%20how%20I%20think%20you%20can%20do%20better"><?php _e( 'Actually, I have a complaint...', 'woocommerce-pdf-invoices-packing-slips' ); ?></a></li>
|
77 |
-
</ul>
|
78 |
-
</div>
|
79 |
-
<!-- Hide extensions ad if this is shown -->
|
80 |
-
<style>.wcpdf-extensions-ad { display: none; }</style>
|
81 |
-
<?php
|
82 |
-
}
|
83 |
-
}
|
84 |
-
}
|
85 |
-
|
86 |
-
public function setup_wizard() {
|
87 |
-
// Setup/welcome
|
88 |
-
if ( ! empty( $_GET['page'] ) ) {
|
89 |
-
switch ( $_GET['page'] ) {
|
90 |
-
case 'wpo-wcpdf-setup' :
|
91 |
-
include_once( WPO_WCPDF()->plugin_path() . '/includes/class-wcpdf-setup-wizard.php' );
|
92 |
-
break;
|
93 |
-
}
|
94 |
-
}
|
95 |
-
}
|
96 |
-
|
97 |
-
public function get_invoice_count() {
|
98 |
-
global $wpdb;
|
99 |
-
$invoice_count = $wpdb->get_var( $wpdb->prepare( "SELECT count(*) FROM {$wpdb->postmeta} WHERE meta_key = %s", '_wcpdf_invoice_number' ) );
|
100 |
-
return (int) $invoice_count;
|
101 |
-
}
|
102 |
-
|
103 |
-
public function update_pdf_counter( $document_type, $document ) {
|
104 |
-
if ( in_array( $document_type, array('invoice','packing-slip') ) ) {
|
105 |
-
$pdf_count = (int) get_option( 'wpo_wcpdf_count_'.$document_type, 0 );
|
106 |
-
update_option( 'wpo_wcpdf_count_'.$document_type, $pdf_count + 1 );
|
107 |
-
}
|
108 |
-
}
|
109 |
-
|
110 |
-
/**
|
111 |
-
* Add PDF actions to the orders listing
|
112 |
-
*/
|
113 |
-
public function add_listing_actions( $order ) {
|
114 |
-
// do not show buttons for trashed orders
|
115 |
-
if ( $order->get_status() == 'trash' ) {
|
116 |
-
return;
|
117 |
-
}
|
118 |
-
|
119 |
-
$listing_actions = array();
|
120 |
-
$documents = WPO_WCPDF()->documents->get_documents();
|
121 |
-
foreach ($documents as $document) {
|
122 |
-
$listing_actions[$document->get_type()] = array(
|
123 |
-
'url' => wp_nonce_url( admin_url( "admin-ajax.php?action=generate_wpo_wcpdf&document_type={$document->get_type()}&order_ids=" . WCX_Order::get_id( $order ) ), 'generate_wpo_wcpdf' ),
|
124 |
-
'img' => !empty($document->icon) ? $document->icon : WPO_WCPDF()->plugin_url() . "/assets/images/generic_document.png",
|
125 |
-
'alt' => "PDF " . $document->get_title(),
|
126 |
-
);
|
127 |
-
}
|
128 |
-
|
129 |
-
$listing_actions = apply_filters( 'wpo_wcpdf_listing_actions', $listing_actions, $order );
|
130 |
-
|
131 |
-
foreach ($listing_actions as $action => $data) {
|
132 |
-
?>
|
133 |
-
<a href="<?php echo $data['url']; ?>" class="button tips wpo_wcpdf <?php echo $action; ?>" target="_blank" alt="<?php echo $data['alt']; ?>" data-tip="<?php echo $data['alt']; ?>">
|
134 |
-
<img src="<?php echo $data['img']; ?>" alt="<?php echo $data['alt']; ?>" width="16">
|
135 |
-
</a>
|
136 |
-
<?php
|
137 |
-
}
|
138 |
-
}
|
139 |
-
|
140 |
-
/**
|
141 |
-
* Create additional Shop Order column for Invoice Numbers
|
142 |
-
* @param array $columns shop order columns
|
143 |
-
*/
|
144 |
-
public function add_invoice_number_column( $columns ) {
|
145 |
-
// get invoice settings
|
146 |
-
$invoice = wcpdf_get_invoice( null );
|
147 |
-
$invoice_settings = $invoice->get_settings();
|
148 |
-
if ( !isset( $invoice_settings['invoice_number_column'] ) ) {
|
149 |
-
return $columns;
|
150 |
-
}
|
151 |
-
|
152 |
-
// put the column after the Status column
|
153 |
-
$new_columns = array_slice($columns, 0, 2, true) +
|
154 |
-
array( 'pdf_invoice_number' => __( 'Invoice Number', 'woocommerce-pdf-invoices-packing-slips' ) ) +
|
155 |
-
array_slice($columns, 2, count($columns) - 1, true) ;
|
156 |
-
return $new_columns;
|
157 |
-
}
|
158 |
-
|
159 |
-
/**
|
160 |
-
* Display Invoice Number in Shop Order column (if available)
|
161 |
-
* @param string $column column slug
|
162 |
-
*/
|
163 |
-
public function invoice_number_column_data( $column ) {
|
164 |
-
global $post, $the_order;
|
165 |
-
|
166 |
-
if ( $column == 'pdf_invoice_number' ) {
|
167 |
-
if ( empty( $the_order ) || WCX_Order::get_id( $the_order ) != $post->ID ) {
|
168 |
-
$order = WCX::get_order( $post->ID );
|
169 |
-
if ( $invoice = wcpdf_get_invoice( $order ) ) {
|
170 |
-
echo $invoice->get_number();
|
171 |
-
}
|
172 |
-
do_action( 'wcpdf_invoice_number_column_end', $order );
|
173 |
-
} else {
|
174 |
-
if ( $invoice = wcpdf_get_invoice( $the_order ) ) {
|
175 |
-
echo $invoice->get_number();
|
176 |
-
}
|
177 |
-
do_action( 'wcpdf_invoice_number_column_end', $the_order );
|
178 |
-
}
|
179 |
-
}
|
180 |
-
}
|
181 |
-
|
182 |
-
/**
|
183 |
-
* Add the meta box on the single order page
|
184 |
-
*/
|
185 |
-
public function add_meta_boxes() {
|
186 |
-
// resend order emails
|
187 |
-
if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '3.2', '>=' ) ) {
|
188 |
-
add_meta_box(
|
189 |
-
'wpo_wcpdf_send_emails',
|
190 |
-
__( 'Send order email', 'woocommerce-pdf-invoices-packing-slips' ),
|
191 |
-
array( $this, 'send_order_email_meta_box' ),
|
192 |
-
'shop_order',
|
193 |
-
'side',
|
194 |
-
'high'
|
195 |
-
);
|
196 |
-
}
|
197 |
-
|
198 |
-
// create PDF buttons
|
199 |
-
add_meta_box(
|
200 |
-
'wpo_wcpdf-box',
|
201 |
-
__( 'Create PDF', 'woocommerce-pdf-invoices-packing-slips' ),
|
202 |
-
array( $this, 'pdf_actions_meta_box' ),
|
203 |
-
'shop_order',
|
204 |
-
'side',
|
205 |
-
'default'
|
206 |
-
);
|
207 |
-
|
208 |
-
// Invoice number & date
|
209 |
-
add_meta_box(
|
210 |
-
'wpo_wcpdf-data-input-box',
|
211 |
-
__( 'PDF Invoice data', 'woocommerce-pdf-invoices-packing-slips' ),
|
212 |
-
array( $this, 'data_input_box_content' ),
|
213 |
-
'shop_order',
|
214 |
-
'normal',
|
215 |
-
'default'
|
216 |
-
);
|
217 |
-
}
|
218 |
-
|
219 |
-
/**
|
220 |
-
* Resend order emails
|
221 |
-
*/
|
222 |
-
public function send_order_email_meta_box( $post ) {
|
223 |
-
?>
|
224 |
-
<ul class="wpo_wcpdf_send_emails submitbox">
|
225 |
-
<li class="wide" id="actions">
|
226 |
-
<select name="wpo_wcpdf_send_emails">
|
227 |
-
<option value=""></option>
|
228 |
-
<?php
|
229 |
-
$mailer = WC()->mailer();
|
230 |
-
$available_emails = apply_filters( 'woocommerce_resend_order_emails_available', array( 'new_order', 'cancelled_order', 'customer_processing_order', 'customer_completed_order', 'customer_invoice' ) );
|
231 |
-
$mails = $mailer->get_emails();
|
232 |
-
if ( ! empty( $mails ) && ! empty( $available_emails ) ) { ?>
|
233 |
-
<?php
|
234 |
-
foreach ( $mails as $mail ) {
|
235 |
-
if ( in_array( $mail->id, $available_emails ) && 'no' !== $mail->enabled ) {
|
236 |
-
echo '<option value="send_email_' . esc_attr( $mail->id ) . '">' . esc_html( $mail->title ) . '</option>';
|
237 |
-
}
|
238 |
-
} ?>
|
239 |
-
<?php
|
240 |
-
}
|
241 |
-
?>
|
242 |
-
</select>
|
243 |
-
<input type="submit" class="button save_order button-primary" name="save" value="<?php esc_attr_e( 'Save order & send email', 'woocommerce-pdf-invoices-packing-slips' ); ?>" />
|
244 |
-
<?php
|
245 |
-
$title = __( 'Send email', 'woocommerce-pdf-invoices-packing-slips' );
|
246 |
-
$url = wp_nonce_url( add_query_arg('wpo_wcpdf_action','resend_email'), 'generate_wpo_wcpdf' );
|
247 |
-
// printf('<a href="%s" class="button wpo_wcpdf_send_email"><span>%s</span></a>')
|
248 |
-
?>
|
249 |
-
</li>
|
250 |
-
</ul>
|
251 |
-
<?php
|
252 |
-
}
|
253 |
-
|
254 |
-
/**
|
255 |
-
* Create the meta box content on the single order page
|
256 |
-
*/
|
257 |
-
public function pdf_actions_meta_box( $post ) {
|
258 |
-
global $post_id;
|
259 |
-
|
260 |
-
$meta_box_actions = array();
|
261 |
-
$documents = WPO_WCPDF()->documents->get_documents();
|
262 |
-
foreach ($documents as $document) {
|
263 |
-
$meta_box_actions[$document->get_type()] = array(
|
264 |
-
'url' => wp_nonce_url( admin_url( "admin-ajax.php?action=generate_wpo_wcpdf&document_type={$document->get_type()}&order_ids=" . $post_id ), 'generate_wpo_wcpdf' ),
|
265 |
-
'alt' => esc_attr( "PDF " . $document->get_title() ),
|
266 |
-
'title' => "PDF " . $document->get_title(),
|
267 |
-
);
|
268 |
-
}
|
269 |
-
|
270 |
-
$meta_box_actions = apply_filters( 'wpo_wcpdf_meta_box_actions', $meta_box_actions, $post_id );
|
271 |
-
|
272 |
-
?>
|
273 |
-
<ul class="wpo_wcpdf-actions">
|
274 |
-
<?php
|
275 |
-
foreach ($meta_box_actions as $document_type => $data) {
|
276 |
-
printf('<li><a href="%1$s" class="button" target="_blank" alt="%2$s">%3$s</a></li>', $data['url'], $data['alt'],$data['title']);
|
277 |
-
}
|
278 |
-
?>
|
279 |
-
</ul>
|
280 |
-
<?php
|
281 |
-
}
|
282 |
-
|
283 |
-
/**
|
284 |
-
* Add metabox for invoice number & date
|
285 |
-
*/
|
286 |
-
public function data_input_box_content ( $post ) {
|
287 |
-
$order = WCX::get_order( $post->ID );
|
288 |
-
|
289 |
-
do_action( 'wpo_wcpdf_meta_box_start', $post->ID );
|
290 |
-
|
291 |
-
if ( $invoice = wcpdf_get_invoice( $order ) ) {
|
292 |
-
$invoice_number = $invoice->get_number();
|
293 |
-
$invoice_date = $invoice->get_date();
|
294 |
-
?>
|
295 |
-
<div class="wcpdf-data-fields">
|
296 |
-
<h4><?php
|
297 |
-
|
298 |
-
<!-- Read only -->
|
299 |
-
<div class="read-only">
|
300 |
-
<?php if ($invoice->exists()) : ?>
|
301 |
-
<div class="invoice-number">
|
302 |
-
<p class="form-field _wcpdf_invoice_number_field ">
|
303 |
-
<p>
|
304 |
-
<span><strong><?php _e( 'Invoice Number', 'woocommerce-pdf-invoices-packing-slips' ); ?>:</strong></span>
|
305 |
-
<span><?php if (!empty($invoice_number)) echo $invoice_number->get_formatted(); ?></span>
|
306 |
-
</p>
|
307 |
-
</p>
|
308 |
-
</div>
|
309 |
-
|
310 |
-
<div class="invoice-date">
|
311 |
-
<p class="form-field form-field-wide">
|
312 |
-
<p>
|
313 |
-
<span><strong><?php _e( 'Invoice Date:', 'woocommerce-pdf-invoices-packing-slips' ); ?></strong></span>
|
314 |
-
<span><?php if (!empty($invoice_date)) echo $invoice_date->date_i18n( wc_date_format().' @ '.wc_time_format() ); ?></span>
|
315 |
-
</p>
|
316 |
-
</p>
|
317 |
-
</div>
|
318 |
-
<?php else : ?>
|
319 |
-
<span class="wpo-wcpdf-set-date-number button"><?php _e( 'Set invoice number & date', 'woocommerce-pdf-invoices-packing-slips' ) ?></span>
|
320 |
-
<?php endif; ?>
|
321 |
-
</div>
|
322 |
-
|
323 |
-
<!-- Editable -->
|
324 |
-
<div class="editable">
|
325 |
-
<p class="form-field _wcpdf_invoice_number_field ">
|
326 |
-
<label for="_wcpdf_invoice_number"><?php _e( 'Invoice Number (unformatted!)', 'woocommerce-pdf-invoices-packing-slips' ); ?>:</label>
|
327 |
-
<?php if ( $invoice->exists() && !empty($invoice_number) ) : ?>
|
328 |
-
<input type="text" class="short" style="" name="_wcpdf_invoice_number" id="_wcpdf_invoice_number" value="<?php echo $invoice_number->get_plain(); ?>" disabled="disabled">
|
329 |
-
<?php else : ?>
|
330 |
-
<input type="text" class="short" style="" name="_wcpdf_invoice_number" id="_wcpdf_invoice_number" value="" disabled="disabled">
|
331 |
-
<?php endif; ?>
|
332 |
-
</p>
|
333 |
-
<p class="form-field form-field-wide">
|
334 |
-
<label for="wcpdf_invoice_date"><?php _e( 'Invoice Date:', 'woocommerce-pdf-invoices-packing-slips' ); ?></label>
|
335 |
-
<?php if ( $invoice->exists() && !empty($invoice_date) ) : ?>
|
336 |
-
<input type="text" class="date-picker-field" name="wcpdf_invoice_date" id="wcpdf_invoice_date" maxlength="10" value="<?php echo $invoice_date->date_i18n( 'Y-m-d' ); ?>" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" disabled="disabled"/>@<input type="number" class="hour" placeholder="<?php _e( 'h', 'woocommerce' ) ?>" name="wcpdf_invoice_date_hour" id="wcpdf_invoice_date_hour" min="0" max="23" size="2" value="<?php echo $invoice_date->date_i18n( 'H' ) ?>" pattern="([01]?[0-9]{1}|2[0-3]{1})" />:<input type="number" class="minute" placeholder="<?php _e( 'm', 'woocommerce' ) ?>" name="wcpdf_invoice_date_minute" id="wcpdf_invoice_date_minute" min="0" max="59" size="2" value="<?php echo $invoice_date->date_i18n( 'i' ); ?>" pattern="[0-5]{1}[0-9]{1}" />
|
337 |
-
<?php else : ?>
|
338 |
-
<input type="text" class="date-picker-field" name="wcpdf_invoice_date" id="wcpdf_invoice_date" maxlength="10" disabled="disabled" value="" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" />@<input type="number" class="hour" disabled="disabled" placeholder="<?php _e( 'h', 'woocommerce' ) ?>" name="wcpdf_invoice_date_hour" id="wcpdf_invoice_date_hour" min="0" max="23" size="2" value="" pattern="([01]?[0-9]{1}|2[0-3]{1})" />:<input type="number" class="minute" placeholder="<?php _e( 'm', 'woocommerce' ) ?>" name="wcpdf_invoice_date_minute" id="wcpdf_invoice_date_minute" min="0" max="59" size="2" value="" pattern="[0-5]{1}[0-9]{1}" disabled="disabled" />
|
339 |
-
<?php endif; ?>
|
340 |
-
</p>
|
341 |
-
</div>
|
342 |
-
</div>
|
343 |
-
<?php
|
344 |
-
}
|
345 |
-
|
346 |
-
do_action( 'wpo_wcpdf_meta_box_end', $post->ID );
|
347 |
-
}
|
348 |
-
|
349 |
-
/**
|
350 |
-
* Add actions to menu
|
351 |
-
*/
|
352 |
-
public function bulk_actions() {
|
353 |
-
if ( $this->is_order_page() ) {
|
354 |
-
$bulk_actions = array();
|
355 |
-
$documents = WPO_WCPDF()->documents->get_documents();
|
356 |
-
foreach ($documents as $document) {
|
357 |
-
$bulk_actions[$document->get_type()] = "PDF " . $document->get_title();
|
358 |
-
}
|
359 |
-
|
360 |
-
$bulk_actions = apply_filters( 'wpo_wcpdf_bulk_actions', $bulk_actions );
|
361 |
-
|
362 |
-
?>
|
363 |
-
<script type="text/javascript">
|
364 |
-
jQuery(document).ready(function() {
|
365 |
-
<?php foreach ($bulk_actions as $action => $title) { ?>
|
366 |
-
jQuery('<option>').val('<?php echo $action; ?>').html('<?php echo esc_attr( $title ); ?>').appendTo("select[name='action'], select[name='action2']");
|
367 |
-
<?php } ?>
|
368 |
-
});
|
369 |
-
</script>
|
370 |
-
<?php
|
371 |
-
}
|
372 |
-
}
|
373 |
-
|
374 |
-
/**
|
375 |
-
* Save invoice number
|
376 |
-
*/
|
377 |
-
public function save_invoice_number_date($post_id) {
|
378 |
-
$post_type = get_post_type( $post_id );
|
379 |
-
if( $post_type == 'shop_order' ) {
|
380 |
-
// bail if this is not an actual 'Save order' action
|
381 |
-
if (!isset($_POST['action']) || $_POST['action'] != 'editpost') {
|
382 |
-
return;
|
383 |
-
}
|
384 |
-
|
385 |
-
$order = WCX::get_order( $post_id );
|
386 |
-
if ( $invoice = wcpdf_get_invoice( $order ) ) {
|
387 |
-
if ( isset( $_POST['wcpdf_invoice_date'] ) ) {
|
388 |
-
$date = $_POST['wcpdf_invoice_date'];
|
389 |
-
$hour = !empty( $_POST['wcpdf_invoice_date_hour'] ) ? $_POST['wcpdf_invoice_date_hour'] : '00';
|
390 |
-
$minute = !empty( $_POST['wcpdf_invoice_date_minute'] ) ? $_POST['wcpdf_invoice_date_minute'] : '00';
|
391 |
-
$invoice_date = "{$date} {$hour}:{$minute}:00";
|
392 |
-
$invoice->set_date( $invoice_date );
|
393 |
-
} elseif ( empty( $_POST['wcpdf_invoice_date'] ) && !empty( $_POST['_wcpdf_invoice_number'] ) ) {
|
394 |
-
$invoice->set_date( current_time( 'timestamp', true ) );
|
395 |
-
}
|
396 |
-
|
397 |
-
if ( isset( $_POST['_wcpdf_invoice_number'] ) ) {
|
398 |
-
$invoice->set_number( $_POST['_wcpdf_invoice_number'] );
|
399 |
-
}
|
400 |
-
|
401 |
-
$invoice->save();
|
402 |
-
}
|
403 |
-
}
|
404 |
-
}
|
405 |
-
|
406 |
-
/**
|
407 |
-
* Send emails manually
|
408 |
-
*/
|
409 |
-
public function send_emails( $post_id, $post ) {
|
410 |
-
if ( ! empty( $_POST['wpo_wcpdf_send_emails'] ) ) {
|
411 |
-
$order = wc_get_order( $post_id );
|
412 |
-
$action = wc_clean( $_POST['wpo_wcpdf_send_emails'] );
|
413 |
-
if ( strstr( $action, 'send_email_' ) ) {
|
414 |
-
// Switch back to the site locale.
|
415 |
-
wc_switch_to_site_locale();
|
416 |
-
do_action( 'woocommerce_before_resend_order_emails', $order );
|
417 |
-
// Ensure gateways are loaded in case they need to insert data into the emails.
|
418 |
-
WC()->payment_gateways();
|
419 |
-
WC()->shipping();
|
420 |
-
// Load mailer.
|
421 |
-
$mailer = WC()->mailer();
|
422 |
-
$email_to_send = str_replace( 'send_email_', '', $action );
|
423 |
-
$mails = $mailer->get_emails();
|
424 |
-
if ( ! empty( $mails ) ) {
|
425 |
-
foreach ( $mails as $mail ) {
|
426 |
-
if ( $mail->id == $email_to_send ) {
|
427 |
-
$mail->trigger( $order->get_id(), $order );
|
428 |
-
/* translators: %s: email title */
|
429 |
-
$order->add_order_note( sprintf( __( '%s email notification manually sent.', 'woocommerce-pdf-invoices-packing-slips' ), $mail->title ), false, true );
|
430 |
-
}
|
431 |
-
}
|
432 |
-
}
|
433 |
-
do_action( 'woocommerce_after_resend_order_email', $order, $email_to_send );
|
434 |
-
// Restore user locale.
|
435 |
-
wc_restore_locale();
|
436 |
-
// Change the post saved message.
|
437 |
-
add_filter( 'redirect_post_location', function( $location ) {
|
438 |
-
// messages in includes/admin/class-wc-admin-post-types.php
|
439 |
-
// 11 => 'Order updated and sent.'
|
440 |
-
return add_query_arg( 'message', 11, $location );
|
441 |
-
} );
|
442 |
-
}
|
443 |
-
}
|
444 |
-
}
|
445 |
-
|
446 |
-
/**
|
447 |
-
* Add invoice number to order search scope
|
448 |
-
*/
|
449 |
-
public function search_fields ( $custom_fields ) {
|
450 |
-
$custom_fields[] = '_wcpdf_invoice_number';
|
451 |
-
$custom_fields[] = '_wcpdf_formatted_invoice_number';
|
452 |
-
return $custom_fields;
|
453 |
-
}
|
454 |
-
|
455 |
-
/**
|
456 |
-
* Check if this is a shop_order page (edit or list)
|
457 |
-
*/
|
458 |
-
public function is_order_page() {
|
459 |
-
global $post_type;
|
460 |
-
if( $post_type == 'shop_order' ) {
|
461 |
-
return true;
|
462 |
-
} else {
|
463 |
-
return false;
|
464 |
-
}
|
465 |
-
}
|
466 |
-
|
467 |
-
/**
|
468 |
-
* Add invoice number to order search scope
|
469 |
-
*/
|
470 |
-
public function invoice_number_column_sortable( $columns ) {
|
471 |
-
$columns['pdf_invoice_number'] = 'pdf_invoice_number';
|
472 |
-
return $columns;
|
473 |
-
}
|
474 |
-
|
475 |
-
public function sort_by_invoice_number( $query ) {
|
476 |
-
if( ! is_admin() )
|
477 |
-
return;
|
478 |
-
$orderby = $query->get( 'orderby');
|
479 |
-
if( 'pdf_invoice_number' == $orderby ) {
|
480 |
-
$query->set('meta_key','_wcpdf_invoice_number');
|
481 |
-
$query->set('orderby','meta_value_num');
|
482 |
-
}
|
483 |
-
}
|
484 |
-
|
485 |
-
}
|
486 |
-
|
487 |
-
endif; // class_exists
|
488 |
-
|
489 |
return new Admin();
|
1 |
+
<?php
|
2 |
+
namespace WPO\WC\PDF_Invoices;
|
3 |
+
|
4 |
+
use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
|
5 |
+
use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
|
6 |
+
use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
|
7 |
+
|
8 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
9 |
+
exit; // Exit if accessed directly
|
10 |
+
}
|
11 |
+
|
12 |
+
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Admin' ) ) :
|
13 |
+
|
14 |
+
class Admin {
|
15 |
+
function __construct() {
|
16 |
+
add_action( 'woocommerce_admin_order_actions_end', array( $this, 'add_listing_actions' ) );
|
17 |
+
add_filter( 'manage_edit-shop_order_columns', array( $this, 'add_invoice_number_column' ), 999 );
|
18 |
+
add_action( 'manage_shop_order_posts_custom_column', array( $this, 'invoice_number_column_data' ), 2 );
|
19 |
+
add_action( 'add_meta_boxes_shop_order', array( $this, 'add_meta_boxes' ) );
|
20 |
+
add_action( 'admin_footer', array( $this, 'bulk_actions' ) );
|
21 |
+
add_filter( 'woocommerce_shop_order_search_fields', array( $this, 'search_fields' ) );
|
22 |
+
|
23 |
+
add_action( 'save_post', array( $this,'save_invoice_number_date' ) );
|
24 |
+
|
25 |
+
// manually send emails
|
26 |
+
// WooCommerce core processes order actions at priority 50
|
27 |
+
add_action( 'woocommerce_process_shop_order_meta', array( $this, 'send_emails' ), 60, 2 );
|
28 |
+
|
29 |
+
add_action( 'admin_notices', array( $this, 'review_plugin_notice' ) );
|
30 |
+
|
31 |
+
add_action( 'init', array( $this, 'setup_wizard') );
|
32 |
+
// add_action( 'wpo_wcpdf_after_pdf', array( $this,'update_pdf_counter' ), 10, 2 );
|
33 |
+
|
34 |
+
add_filter( 'manage_edit-shop_order_sortable_columns', array( $this, 'invoice_number_column_sortable' ) );
|
35 |
+
add_filter( 'pre_get_posts', array( $this, 'sort_by_invoice_number' ) );
|
36 |
+
}
|
37 |
+
|
38 |
+
// display review admin notice after 100 pdf downloads
|
39 |
+
public function review_plugin_notice() {
|
40 |
+
if ( $this->is_order_page() === false && !( isset( $_GET['page'] ) && $_GET['page'] == 'wpo_wcpdf_options_page' ) ) {
|
41 |
+
return;
|
42 |
+
}
|
43 |
+
|
44 |
+
if ( get_option( 'wpo_wcpdf_review_notice_dismissed' ) !== false ) {
|
45 |
+
return;
|
46 |
+
} else {
|
47 |
+
if ( isset( $_GET['wpo_wcpdf_dismis_review'] ) ) {
|
48 |
+
update_option( 'wpo_wcpdf_review_notice_dismissed', true );
|
49 |
+
return;
|
50 |
+
}
|
51 |
+
|
52 |
+
// keep track of how many days this notice is show so we can remove it after 7 days
|
53 |
+
$notice_shown_on = get_option( 'wpo_wcpdf_review_notice_shown', array() );
|
54 |
+
$today = date('Y-m-d');
|
55 |
+
if ( !in_array($today, $notice_shown_on) ) {
|
56 |
+
$notice_shown_on[] = $today;
|
57 |
+
update_option( 'wpo_wcpdf_review_notice_shown', $notice_shown_on );
|
58 |
+
}
|
59 |
+
// count number of days review is shown, dismiss forever if shown more than 7
|
60 |
+
if (count($notice_shown_on) > 7) {
|
61 |
+
update_option( 'wpo_wcpdf_review_notice_dismissed', true );
|
62 |
+
return;
|
63 |
+
}
|
64 |
+
|
65 |
+
// get invoice count to determine whether notice should be shown
|
66 |
+
$invoice_count = $this->get_invoice_count();
|
67 |
+
if ( $invoice_count > 100 ) {
|
68 |
+
$rounded_count = (int) substr( (string) $invoice_count, 0, 1 ) * pow( 10, strlen( (string) $invoice_count ) - 1);
|
69 |
+
?>
|
70 |
+
<div class="notice notice-info is-dismissible wpo-wcpdf-review-notice">
|
71 |
+
<h3><?php printf( __( 'Wow, you have created more than %d invoices with our plugin!', 'woocommerce-pdf-invoices-packing-slips' ), $rounded_count ); ?></h3>
|
72 |
+
<p><?php _e( 'It would mean a lot to us if you would quickly give our plugin a 5-star rating. Help us spread the word and boost our motivation!', 'woocommerce-pdf-invoices-packing-slips' ); ?></p>
|
73 |
+
<ul>
|
74 |
+
<li><a href="https://wordpress.org/support/plugin/woocommerce-pdf-invoices-packing-slips/reviews/?rate=5#new-post" class="button"><?php _e( 'Yes you deserve it!', 'woocommerce-pdf-invoices-packing-slips' ); ?></span></a></li>
|
75 |
+
<li><a href="<?php echo esc_url( add_query_arg( 'wpo_wcpdf_dismis_review', true ) ); ?>" class="wpo-wcpdf-dismiss"><?php _e( 'Hide this message', 'woocommerce-pdf-invoices-packing-slips' ); ?> / <?php _e( 'Already did!', 'woocommerce-pdf-invoices-packing-slips' ); ?></a></li>
|
76 |
+
<li><a href="mailto:support@wpovernight.com?Subject=Here%20is%20how%20I%20think%20you%20can%20do%20better"><?php _e( 'Actually, I have a complaint...', 'woocommerce-pdf-invoices-packing-slips' ); ?></a></li>
|
77 |
+
</ul>
|
78 |
+
</div>
|
79 |
+
<!-- Hide extensions ad if this is shown -->
|
80 |
+
<style>.wcpdf-extensions-ad { display: none; }</style>
|
81 |
+
<?php
|
82 |
+
}
|
83 |
+
}
|
84 |
+
}
|
85 |
+
|
86 |
+
public function setup_wizard() {
|
87 |
+
// Setup/welcome
|
88 |
+
if ( ! empty( $_GET['page'] ) ) {
|
89 |
+
switch ( $_GET['page'] ) {
|
90 |
+
case 'wpo-wcpdf-setup' :
|
91 |
+
include_once( WPO_WCPDF()->plugin_path() . '/includes/class-wcpdf-setup-wizard.php' );
|
92 |
+
break;
|
93 |
+
}
|
94 |
+
}
|
95 |
+
}
|
96 |
+
|
97 |
+
public function get_invoice_count() {
|
98 |
+
global $wpdb;
|
99 |
+
$invoice_count = $wpdb->get_var( $wpdb->prepare( "SELECT count(*) FROM {$wpdb->postmeta} WHERE meta_key = %s", '_wcpdf_invoice_number' ) );
|
100 |
+
return (int) $invoice_count;
|
101 |
+
}
|
102 |
+
|
103 |
+
public function update_pdf_counter( $document_type, $document ) {
|
104 |
+
if ( in_array( $document_type, array('invoice','packing-slip') ) ) {
|
105 |
+
$pdf_count = (int) get_option( 'wpo_wcpdf_count_'.$document_type, 0 );
|
106 |
+
update_option( 'wpo_wcpdf_count_'.$document_type, $pdf_count + 1 );
|
107 |
+
}
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Add PDF actions to the orders listing
|
112 |
+
*/
|
113 |
+
public function add_listing_actions( $order ) {
|
114 |
+
// do not show buttons for trashed orders
|
115 |
+
if ( $order->get_status() == 'trash' ) {
|
116 |
+
return;
|
117 |
+
}
|
118 |
+
|
119 |
+
$listing_actions = array();
|
120 |
+
$documents = WPO_WCPDF()->documents->get_documents();
|
121 |
+
foreach ($documents as $document) {
|
122 |
+
$listing_actions[$document->get_type()] = array(
|
123 |
+
'url' => wp_nonce_url( admin_url( "admin-ajax.php?action=generate_wpo_wcpdf&document_type={$document->get_type()}&order_ids=" . WCX_Order::get_id( $order ) ), 'generate_wpo_wcpdf' ),
|
124 |
+
'img' => !empty($document->icon) ? $document->icon : WPO_WCPDF()->plugin_url() . "/assets/images/generic_document.png",
|
125 |
+
'alt' => "PDF " . $document->get_title(),
|
126 |
+
);
|
127 |
+
}
|
128 |
+
|
129 |
+
$listing_actions = apply_filters( 'wpo_wcpdf_listing_actions', $listing_actions, $order );
|
130 |
+
|
131 |
+
foreach ($listing_actions as $action => $data) {
|
132 |
+
?>
|
133 |
+
<a href="<?php echo $data['url']; ?>" class="button tips wpo_wcpdf <?php echo $action; ?>" target="_blank" alt="<?php echo $data['alt']; ?>" data-tip="<?php echo $data['alt']; ?>">
|
134 |
+
<img src="<?php echo $data['img']; ?>" alt="<?php echo $data['alt']; ?>" width="16">
|
135 |
+
</a>
|
136 |
+
<?php
|
137 |
+
}
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Create additional Shop Order column for Invoice Numbers
|
142 |
+
* @param array $columns shop order columns
|
143 |
+
*/
|
144 |
+
public function add_invoice_number_column( $columns ) {
|
145 |
+
// get invoice settings
|
146 |
+
$invoice = wcpdf_get_invoice( null );
|
147 |
+
$invoice_settings = $invoice->get_settings();
|
148 |
+
if ( !isset( $invoice_settings['invoice_number_column'] ) ) {
|
149 |
+
return $columns;
|
150 |
+
}
|
151 |
+
|
152 |
+
// put the column after the Status column
|
153 |
+
$new_columns = array_slice($columns, 0, 2, true) +
|
154 |
+
array( 'pdf_invoice_number' => __( 'Invoice Number', 'woocommerce-pdf-invoices-packing-slips' ) ) +
|
155 |
+
array_slice($columns, 2, count($columns) - 1, true) ;
|
156 |
+
return $new_columns;
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Display Invoice Number in Shop Order column (if available)
|
161 |
+
* @param string $column column slug
|
162 |
+
*/
|
163 |
+
public function invoice_number_column_data( $column ) {
|
164 |
+
global $post, $the_order;
|
165 |
+
|
166 |
+
if ( $column == 'pdf_invoice_number' ) {
|
167 |
+
if ( empty( $the_order ) || WCX_Order::get_id( $the_order ) != $post->ID ) {
|
168 |
+
$order = WCX::get_order( $post->ID );
|
169 |
+
if ( $invoice = wcpdf_get_invoice( $order ) ) {
|
170 |
+
echo $invoice->get_number();
|
171 |
+
}
|
172 |
+
do_action( 'wcpdf_invoice_number_column_end', $order );
|
173 |
+
} else {
|
174 |
+
if ( $invoice = wcpdf_get_invoice( $the_order ) ) {
|
175 |
+
echo $invoice->get_number();
|
176 |
+
}
|
177 |
+
do_action( 'wcpdf_invoice_number_column_end', $the_order );
|
178 |
+
}
|
179 |
+
}
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Add the meta box on the single order page
|
184 |
+
*/
|
185 |
+
public function add_meta_boxes() {
|
186 |
+
// resend order emails
|
187 |
+
if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '3.2', '>=' ) ) {
|
188 |
+
add_meta_box(
|
189 |
+
'wpo_wcpdf_send_emails',
|
190 |
+
__( 'Send order email', 'woocommerce-pdf-invoices-packing-slips' ),
|
191 |
+
array( $this, 'send_order_email_meta_box' ),
|
192 |
+
'shop_order',
|
193 |
+
'side',
|
194 |
+
'high'
|
195 |
+
);
|
196 |
+
}
|
197 |
+
|
198 |
+
// create PDF buttons
|
199 |
+
add_meta_box(
|
200 |
+
'wpo_wcpdf-box',
|
201 |
+
__( 'Create PDF', 'woocommerce-pdf-invoices-packing-slips' ),
|
202 |
+
array( $this, 'pdf_actions_meta_box' ),
|
203 |
+
'shop_order',
|
204 |
+
'side',
|
205 |
+
'default'
|
206 |
+
);
|
207 |
+
|
208 |
+
// Invoice number & date
|
209 |
+
add_meta_box(
|
210 |
+
'wpo_wcpdf-data-input-box',
|
211 |
+
__( 'PDF Invoice data', 'woocommerce-pdf-invoices-packing-slips' ),
|
212 |
+
array( $this, 'data_input_box_content' ),
|
213 |
+
'shop_order',
|
214 |
+
'normal',
|
215 |
+
'default'
|
216 |
+
);
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Resend order emails
|
221 |
+
*/
|
222 |
+
public function send_order_email_meta_box( $post ) {
|
223 |
+
?>
|
224 |
+
<ul class="wpo_wcpdf_send_emails submitbox">
|
225 |
+
<li class="wide" id="actions">
|
226 |
+
<select name="wpo_wcpdf_send_emails">
|
227 |
+
<option value=""></option>
|
228 |
+
<?php
|
229 |
+
$mailer = WC()->mailer();
|
230 |
+
$available_emails = apply_filters( 'woocommerce_resend_order_emails_available', array( 'new_order', 'cancelled_order', 'customer_processing_order', 'customer_completed_order', 'customer_invoice' ) );
|
231 |
+
$mails = $mailer->get_emails();
|
232 |
+
if ( ! empty( $mails ) && ! empty( $available_emails ) ) { ?>
|
233 |
+
<?php
|
234 |
+
foreach ( $mails as $mail ) {
|
235 |
+
if ( in_array( $mail->id, $available_emails ) && 'no' !== $mail->enabled ) {
|
236 |
+
echo '<option value="send_email_' . esc_attr( $mail->id ) . '">' . esc_html( $mail->title ) . '</option>';
|
237 |
+
}
|
238 |
+
} ?>
|
239 |
+
<?php
|
240 |
+
}
|
241 |
+
?>
|
242 |
+
</select>
|
243 |
+
<input type="submit" class="button save_order button-primary" name="save" value="<?php esc_attr_e( 'Save order & send email', 'woocommerce-pdf-invoices-packing-slips' ); ?>" />
|
244 |
+
<?php
|
245 |
+
$title = __( 'Send email', 'woocommerce-pdf-invoices-packing-slips' );
|
246 |
+
$url = wp_nonce_url( add_query_arg('wpo_wcpdf_action','resend_email'), 'generate_wpo_wcpdf' );
|
247 |
+
// printf('<a href="%s" class="button wpo_wcpdf_send_email"><span>%s</span></a>')
|
248 |
+
?>
|
249 |
+
</li>
|
250 |
+
</ul>
|
251 |
+
<?php
|
252 |
+
}
|
253 |
+
|
254 |
+
/**
|
255 |
+
* Create the meta box content on the single order page
|
256 |
+
*/
|
257 |
+
public function pdf_actions_meta_box( $post ) {
|
258 |
+
global $post_id;
|
259 |
+
|
260 |
+
$meta_box_actions = array();
|
261 |
+
$documents = WPO_WCPDF()->documents->get_documents();
|
262 |
+
foreach ($documents as $document) {
|
263 |
+
$meta_box_actions[$document->get_type()] = array(
|
264 |
+
'url' => wp_nonce_url( admin_url( "admin-ajax.php?action=generate_wpo_wcpdf&document_type={$document->get_type()}&order_ids=" . $post_id ), 'generate_wpo_wcpdf' ),
|
265 |
+
'alt' => esc_attr( "PDF " . $document->get_title() ),
|
266 |
+
'title' => "PDF " . $document->get_title(),
|
267 |
+
);
|
268 |
+
}
|
269 |
+
|
270 |
+
$meta_box_actions = apply_filters( 'wpo_wcpdf_meta_box_actions', $meta_box_actions, $post_id );
|
271 |
+
|
272 |
+
?>
|
273 |
+
<ul class="wpo_wcpdf-actions">
|
274 |
+
<?php
|
275 |
+
foreach ($meta_box_actions as $document_type => $data) {
|
276 |
+
printf('<li><a href="%1$s" class="button" target="_blank" alt="%2$s">%3$s</a></li>', $data['url'], $data['alt'],$data['title']);
|
277 |
+
}
|
278 |
+
?>
|
279 |
+
</ul>
|
280 |
+
<?php
|
281 |
+
}
|
282 |
+
|
283 |
+
/**
|
284 |
+
* Add metabox for invoice number & date
|
285 |
+
*/
|
286 |
+
public function data_input_box_content ( $post ) {
|
287 |
+
$order = WCX::get_order( $post->ID );
|
288 |
+
|
289 |
+
do_action( 'wpo_wcpdf_meta_box_start', $post->ID );
|
290 |
+
|
291 |
+
if ( $invoice = wcpdf_get_invoice( $order ) ) {
|
292 |
+
$invoice_number = $invoice->get_number();
|
293 |
+
$invoice_date = $invoice->get_date();
|
294 |
+
?>
|
295 |
+
<div class="wcpdf-data-fields">
|
296 |
+
<h4><?php echo $invoice->get_title(); ?><?php if ($invoice->exists()) : ?><span id="" class="wpo-wcpdf-edit-date-number dashicons dashicons-edit"></span><?php endif; ?></h4>
|
297 |
+
|
298 |
+
<!-- Read only -->
|
299 |
+
<div class="read-only">
|
300 |
+
<?php if ($invoice->exists()) : ?>
|
301 |
+
<div class="invoice-number">
|
302 |
+
<p class="form-field _wcpdf_invoice_number_field ">
|
303 |
+
<p>
|
304 |
+
<span><strong><?php _e( 'Invoice Number', 'woocommerce-pdf-invoices-packing-slips' ); ?>:</strong></span>
|
305 |
+
<span><?php if (!empty($invoice_number)) echo $invoice_number->get_formatted(); ?></span>
|
306 |
+
</p>
|
307 |
+
</p>
|
308 |
+
</div>
|
309 |
+
|
310 |
+
<div class="invoice-date">
|
311 |
+
<p class="form-field form-field-wide">
|
312 |
+
<p>
|
313 |
+
<span><strong><?php _e( 'Invoice Date:', 'woocommerce-pdf-invoices-packing-slips' ); ?></strong></span>
|
314 |
+
<span><?php if (!empty($invoice_date)) echo $invoice_date->date_i18n( wc_date_format().' @ '.wc_time_format() ); ?></span>
|
315 |
+
</p>
|
316 |
+
</p>
|
317 |
+
</div>
|
318 |
+
<?php else : ?>
|
319 |
+
<span class="wpo-wcpdf-set-date-number button"><?php _e( 'Set invoice number & date', 'woocommerce-pdf-invoices-packing-slips' ) ?></span>
|
320 |
+
<?php endif; ?>
|
321 |
+
</div>
|
322 |
+
|
323 |
+
<!-- Editable -->
|
324 |
+
<div class="editable">
|
325 |
+
<p class="form-field _wcpdf_invoice_number_field ">
|
326 |
+
<label for="_wcpdf_invoice_number"><?php _e( 'Invoice Number (unformatted!)', 'woocommerce-pdf-invoices-packing-slips' ); ?>:</label>
|
327 |
+
<?php if ( $invoice->exists() && !empty($invoice_number) ) : ?>
|
328 |
+
<input type="text" class="short" style="" name="_wcpdf_invoice_number" id="_wcpdf_invoice_number" value="<?php echo $invoice_number->get_plain(); ?>" disabled="disabled">
|
329 |
+
<?php else : ?>
|
330 |
+
<input type="text" class="short" style="" name="_wcpdf_invoice_number" id="_wcpdf_invoice_number" value="" disabled="disabled">
|
331 |
+
<?php endif; ?>
|
332 |
+
</p>
|
333 |
+
<p class="form-field form-field-wide">
|
334 |
+
<label for="wcpdf_invoice_date"><?php _e( 'Invoice Date:', 'woocommerce-pdf-invoices-packing-slips' ); ?></label>
|
335 |
+
<?php if ( $invoice->exists() && !empty($invoice_date) ) : ?>
|
336 |
+
<input type="text" class="date-picker-field" name="wcpdf_invoice_date" id="wcpdf_invoice_date" maxlength="10" value="<?php echo $invoice_date->date_i18n( 'Y-m-d' ); ?>" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" disabled="disabled"/>@<input type="number" class="hour" placeholder="<?php _e( 'h', 'woocommerce' ) ?>" name="wcpdf_invoice_date_hour" id="wcpdf_invoice_date_hour" min="0" max="23" size="2" value="<?php echo $invoice_date->date_i18n( 'H' ) ?>" pattern="([01]?[0-9]{1}|2[0-3]{1})" />:<input type="number" class="minute" placeholder="<?php _e( 'm', 'woocommerce' ) ?>" name="wcpdf_invoice_date_minute" id="wcpdf_invoice_date_minute" min="0" max="59" size="2" value="<?php echo $invoice_date->date_i18n( 'i' ); ?>" pattern="[0-5]{1}[0-9]{1}" />
|
337 |
+
<?php else : ?>
|
338 |
+
<input type="text" class="date-picker-field" name="wcpdf_invoice_date" id="wcpdf_invoice_date" maxlength="10" disabled="disabled" value="" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" />@<input type="number" class="hour" disabled="disabled" placeholder="<?php _e( 'h', 'woocommerce' ) ?>" name="wcpdf_invoice_date_hour" id="wcpdf_invoice_date_hour" min="0" max="23" size="2" value="" pattern="([01]?[0-9]{1}|2[0-3]{1})" />:<input type="number" class="minute" placeholder="<?php _e( 'm', 'woocommerce' ) ?>" name="wcpdf_invoice_date_minute" id="wcpdf_invoice_date_minute" min="0" max="59" size="2" value="" pattern="[0-5]{1}[0-9]{1}" disabled="disabled" />
|
339 |
+
<?php endif; ?>
|
340 |
+
</p>
|
341 |
+
</div>
|
342 |
+
</div>
|
343 |
+
<?php
|
344 |
+
}
|
345 |
+
|
346 |
+
do_action( 'wpo_wcpdf_meta_box_end', $post->ID );
|
347 |
+
}
|
348 |
+
|
349 |
+
/**
|
350 |
+
* Add actions to menu
|
351 |
+
*/
|
352 |
+
public function bulk_actions() {
|
353 |
+
if ( $this->is_order_page() ) {
|
354 |
+
$bulk_actions = array();
|
355 |
+
$documents = WPO_WCPDF()->documents->get_documents();
|
356 |
+
foreach ($documents as $document) {
|
357 |
+
$bulk_actions[$document->get_type()] = "PDF " . $document->get_title();
|
358 |
+
}
|
359 |
+
|
360 |
+
$bulk_actions = apply_filters( 'wpo_wcpdf_bulk_actions', $bulk_actions );
|
361 |
+
|
362 |
+
?>
|
363 |
+
<script type="text/javascript">
|
364 |
+
jQuery(document).ready(function() {
|
365 |
+
<?php foreach ($bulk_actions as $action => $title) { ?>
|
366 |
+
jQuery('<option>').val('<?php echo $action; ?>').html('<?php echo esc_attr( $title ); ?>').appendTo("select[name='action'], select[name='action2']");
|
367 |
+
<?php } ?>
|
368 |
+
});
|
369 |
+
</script>
|
370 |
+
<?php
|
371 |
+
}
|
372 |
+
}
|
373 |
+
|
374 |
+
/**
|
375 |
+
* Save invoice number
|
376 |
+
*/
|
377 |
+
public function save_invoice_number_date($post_id) {
|
378 |
+
$post_type = get_post_type( $post_id );
|
379 |
+
if( $post_type == 'shop_order' ) {
|
380 |
+
// bail if this is not an actual 'Save order' action
|
381 |
+
if (!isset($_POST['action']) || $_POST['action'] != 'editpost') {
|
382 |
+
return;
|
383 |
+
}
|
384 |
+
|
385 |
+
$order = WCX::get_order( $post_id );
|
386 |
+
if ( $invoice = wcpdf_get_invoice( $order ) ) {
|
387 |
+
if ( isset( $_POST['wcpdf_invoice_date'] ) ) {
|
388 |
+
$date = $_POST['wcpdf_invoice_date'];
|
389 |
+
$hour = !empty( $_POST['wcpdf_invoice_date_hour'] ) ? $_POST['wcpdf_invoice_date_hour'] : '00';
|
390 |
+
$minute = !empty( $_POST['wcpdf_invoice_date_minute'] ) ? $_POST['wcpdf_invoice_date_minute'] : '00';
|
391 |
+
$invoice_date = "{$date} {$hour}:{$minute}:00";
|
392 |
+
$invoice->set_date( $invoice_date );
|
393 |
+
} elseif ( empty( $_POST['wcpdf_invoice_date'] ) && !empty( $_POST['_wcpdf_invoice_number'] ) ) {
|
394 |
+
$invoice->set_date( current_time( 'timestamp', true ) );
|
395 |
+
}
|
396 |
+
|
397 |
+
if ( isset( $_POST['_wcpdf_invoice_number'] ) ) {
|
398 |
+
$invoice->set_number( $_POST['_wcpdf_invoice_number'] );
|
399 |
+
}
|
400 |
+
|
401 |
+
$invoice->save();
|
402 |
+
}
|
403 |
+
}
|
404 |
+
}
|
405 |
+
|
406 |
+
/**
|
407 |
+
* Send emails manually
|
408 |
+
*/
|
409 |
+
public function send_emails( $post_id, $post ) {
|
410 |
+
if ( ! empty( $_POST['wpo_wcpdf_send_emails'] ) ) {
|
411 |
+
$order = wc_get_order( $post_id );
|
412 |
+
$action = wc_clean( $_POST['wpo_wcpdf_send_emails'] );
|
413 |
+
if ( strstr( $action, 'send_email_' ) ) {
|
414 |
+
// Switch back to the site locale.
|
415 |
+
wc_switch_to_site_locale();
|
416 |
+
do_action( 'woocommerce_before_resend_order_emails', $order );
|
417 |
+
// Ensure gateways are loaded in case they need to insert data into the emails.
|
418 |
+
WC()->payment_gateways();
|
419 |
+
WC()->shipping();
|
420 |
+
// Load mailer.
|
421 |
+
$mailer = WC()->mailer();
|
422 |
+
$email_to_send = str_replace( 'send_email_', '', $action );
|
423 |
+
$mails = $mailer->get_emails();
|
424 |
+
if ( ! empty( $mails ) ) {
|
425 |
+
foreach ( $mails as $mail ) {
|
426 |
+
if ( $mail->id == $email_to_send ) {
|
427 |
+
$mail->trigger( $order->get_id(), $order );
|
428 |
+
/* translators: %s: email title */
|
429 |
+
$order->add_order_note( sprintf( __( '%s email notification manually sent.', 'woocommerce-pdf-invoices-packing-slips' ), $mail->title ), false, true );
|
430 |
+
}
|
431 |
+
}
|
432 |
+
}
|
433 |
+
do_action( 'woocommerce_after_resend_order_email', $order, $email_to_send );
|
434 |
+
// Restore user locale.
|
435 |
+
wc_restore_locale();
|
436 |
+
// Change the post saved message.
|
437 |
+
add_filter( 'redirect_post_location', function( $location ) {
|
438 |
+
// messages in includes/admin/class-wc-admin-post-types.php
|
439 |
+
// 11 => 'Order updated and sent.'
|
440 |
+
return add_query_arg( 'message', 11, $location );
|
441 |
+
} );
|
442 |
+
}
|
443 |
+
}
|
444 |
+
}
|
445 |
+
|
446 |
+
/**
|
447 |
+
* Add invoice number to order search scope
|
448 |
+
*/
|
449 |
+
public function search_fields ( $custom_fields ) {
|
450 |
+
$custom_fields[] = '_wcpdf_invoice_number';
|
451 |
+
$custom_fields[] = '_wcpdf_formatted_invoice_number';
|
452 |
+
return $custom_fields;
|
453 |
+
}
|
454 |
+
|
455 |
+
/**
|
456 |
+
* Check if this is a shop_order page (edit or list)
|
457 |
+
*/
|
458 |
+
public function is_order_page() {
|
459 |
+
global $post_type;
|
460 |
+
if( $post_type == 'shop_order' ) {
|
461 |
+
return true;
|
462 |
+
} else {
|
463 |
+
return false;
|
464 |
+
}
|
465 |
+
}
|
466 |
+
|
467 |
+
/**
|
468 |
+
* Add invoice number to order search scope
|
469 |
+
*/
|
470 |
+
public function invoice_number_column_sortable( $columns ) {
|
471 |
+
$columns['pdf_invoice_number'] = 'pdf_invoice_number';
|
472 |
+
return $columns;
|
473 |
+
}
|
474 |
+
|
475 |
+
public function sort_by_invoice_number( $query ) {
|
476 |
+
if( ! is_admin() )
|
477 |
+
return;
|
478 |
+
$orderby = $query->get( 'orderby');
|
479 |
+
if( 'pdf_invoice_number' == $orderby ) {
|
480 |
+
$query->set('meta_key','_wcpdf_invoice_number');
|
481 |
+
$query->set('orderby','meta_value_num');
|
482 |
+
}
|
483 |
+
}
|
484 |
+
|
485 |
+
}
|
486 |
+
|
487 |
+
endif; // class_exists
|
488 |
+
|
489 |
return new Admin();
|
includes/class-wcpdf-frontend.php
CHANGED
@@ -50,9 +50,15 @@ class Frontend {
|
|
50 |
|
51 |
// Check if invoice has been created already or if status allows download (filter your own array of allowed statuses)
|
52 |
if ( $invoice_allowed || in_array(WCX_Order::get_status( $order ), apply_filters( 'wpo_wcpdf_myaccount_allowed_order_statuses', array() ) ) ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
$actions['invoice'] = array(
|
54 |
'url' => $pdf_url,
|
55 |
-
'name' => apply_filters( 'wpo_wcpdf_myaccount_button_text',
|
56 |
);
|
57 |
}
|
58 |
}
|
50 |
|
51 |
// Check if invoice has been created already or if status allows download (filter your own array of allowed statuses)
|
52 |
if ( $invoice_allowed || in_array(WCX_Order::get_status( $order ), apply_filters( 'wpo_wcpdf_myaccount_allowed_order_statuses', array() ) ) ) {
|
53 |
+
$document_title = $invoice->get_setting('title');
|
54 |
+
if ( !empty($document_title) && !empty(array_filter($document_title)) ) {
|
55 |
+
$button_text = sprintf ( __( 'Download %s (PDF)', 'woocommerce-pdf-invoices-packing-slips' ), $invoice->get_title() );
|
56 |
+
} else {
|
57 |
+
$button_text = __( 'Download invoice (PDF)', 'woocommerce-pdf-invoices-packing-slips' );
|
58 |
+
}
|
59 |
$actions['invoice'] = array(
|
60 |
'url' => $pdf_url,
|
61 |
+
'name' => apply_filters( 'wpo_wcpdf_myaccount_button_text', $button_text, $invoice )
|
62 |
);
|
63 |
}
|
64 |
}
|
includes/class-wcpdf-install.php
CHANGED
@@ -1,359 +1,359 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPO\WC\PDF_Invoices;
|
3 |
-
|
4 |
-
use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
|
5 |
-
use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
|
6 |
-
use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
|
7 |
-
|
8 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
9 |
-
exit; // Exit if accessed directly
|
10 |
-
}
|
11 |
-
|
12 |
-
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Install' ) ) :
|
13 |
-
|
14 |
-
class Install {
|
15 |
-
|
16 |
-
function __construct() {
|
17 |
-
// run lifecycle methods
|
18 |
-
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
|
19 |
-
add_action( 'wp_loaded', array( $this, 'do_install' ) );
|
20 |
-
}
|
21 |
-
}
|
22 |
-
|
23 |
-
/** Lifecycle methods *******************************************************
|
24 |
-
* Because register_activation_hook only runs when the plugin is manually
|
25 |
-
* activated by the user, we're checking the current version against the
|
26 |
-
* version stored in the database
|
27 |
-
****************************************************************************/
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Handles version checking
|
31 |
-
*/
|
32 |
-
public function do_install() {
|
33 |
-
// only install when woocommerce is active
|
34 |
-
if ( !WPO_WCPDF()->is_woocommerce_activated() ) {
|
35 |
-
return;
|
36 |
-
}
|
37 |
-
|
38 |
-
$version_setting = 'wpo_wcpdf_version';
|
39 |
-
$installed_version = get_option( $version_setting );
|
40 |
-
|
41 |
-
// installed version lower than plugin version?
|
42 |
-
if ( version_compare( $installed_version, WPO_WCPDF_VERSION, '<' ) ) {
|
43 |
-
|
44 |
-
if ( ! $installed_version ) {
|
45 |
-
$this->install();
|
46 |
-
} else {
|
47 |
-
$this->upgrade( $installed_version );
|
48 |
-
}
|
49 |
-
|
50 |
-
// new version number
|
51 |
-
update_option( $version_setting, WPO_WCPDF_VERSION );
|
52 |
-
} elseif ( $installed_version && version_compare( $installed_version, WPO_WCPDF_VERSION, '>' ) ) {
|
53 |
-
$this->downgrade( $installed_version );
|
54 |
-
// downgrade version number
|
55 |
-
update_option( $version_setting, WPO_WCPDF_VERSION );
|
56 |
-
}
|
57 |
-
}
|
58 |
-
|
59 |
-
|
60 |
-
/**
|
61 |
-
* Plugin install method. Perform any installation tasks here
|
62 |
-
*/
|
63 |
-
protected function install() {
|
64 |
-
// only install when php 5.3 or higher
|
65 |
-
if ( version_compare( PHP_VERSION, '5.3', '<' ) ) {
|
66 |
-
return;
|
67 |
-
}
|
68 |
-
|
69 |
-
// check if upgrading from versionless (1.4.14 and older)
|
70 |
-
if ( get_option('wpo_wcpdf_general_settings') ) {
|
71 |
-
$this->upgrade( 'versionless' );
|
72 |
-
return;
|
73 |
-
}
|
74 |
-
|
75 |
-
// Create temp folders
|
76 |
-
$tmp_base = WPO_WCPDF()->main->get_tmp_base();
|
77 |
-
|
78 |
-
// check if tmp folder exists => if not, initialize
|
79 |
-
if ( !@is_dir( $tmp_base ) ) {
|
80 |
-
WPO_WCPDF()->main->init_tmp( $tmp_base );
|
81 |
-
}
|
82 |
-
|
83 |
-
// Unsupported currency symbols
|
84 |
-
$unsupported_symbols = array (
|
85 |
-
'AED',
|
86 |
-
'AFN',
|
87 |
-
'BDT',
|
88 |
-
'BHD',
|
89 |
-
'BTC',
|
90 |
-
'CRC',
|
91 |
-
'DZD',
|
92 |
-
'GEL',
|
93 |
-
'GHS',
|
94 |
-
'ILS',
|
95 |
-
'INR',
|
96 |
-
'IQD',
|
97 |
-
'IRR',
|
98 |
-
'IRT',
|
99 |
-
'JOD',
|
100 |
-
'KHR',
|
101 |
-
'KPW',
|
102 |
-
'KRW',
|
103 |
-
'KWD',
|
104 |
-
'LAK',
|
105 |
-
'LBP',
|
106 |
-
'LKR',
|
107 |
-
'LYD',
|
108 |
-
'MAD',
|
109 |
-
'MNT',
|
110 |
-
'MUR',
|
111 |
-
'MVR',
|
112 |
-
'NPR',
|
113 |
-
'OMR',
|
114 |
-
'PHP',
|
115 |
-
'PKR',
|
116 |
-
'PYG',
|
117 |
-
'QAR',
|
118 |
-
'RUB',
|
119 |
-
'SAR',
|
120 |
-
'SCR',
|
121 |
-
'SDG',
|
122 |
-
'SYP',
|
123 |
-
'THB',
|
124 |
-
'TND',
|
125 |
-
'TRY',
|
126 |
-
'UAH',
|
127 |
-
'YER',
|
128 |
-
);
|
129 |
-
|
130 |
-
// set default settings
|
131 |
-
$settings_defaults = array(
|
132 |
-
'wpo_wcpdf_settings_general' => array(
|
133 |
-
'download_display' => 'display',
|
134 |
-
'template_path' => WPO_WCPDF()->plugin_path() . '/templates/Simple',
|
135 |
-
'currency_font' => ( in_array( get_woocommerce_currency(), $unsupported_symbols ) ) ? 1 : '',
|
136 |
-
'paper_size' => 'a4',
|
137 |
-
// 'header_logo' => '',
|
138 |
-
// 'shop_name' => array(),
|
139 |
-
// 'shop_address' => array(),
|
140 |
-
// 'footer' => array(),
|
141 |
-
// 'extra_1' => array(),
|
142 |
-
// 'extra_2' => array(),
|
143 |
-
// 'extra_3' => array(),
|
144 |
-
),
|
145 |
-
'wpo_wcpdf_documents_settings_invoice' => array(
|
146 |
-
'enabled' => 1,
|
147 |
-
// 'attach_to_email_ids' => array(),
|
148 |
-
// 'display_shipping_address' => '',
|
149 |
-
// 'display_email' => '',
|
150 |
-
// 'display_phone' => '',
|
151 |
-
// 'display_date' => '',
|
152 |
-
// 'display_number' => '',
|
153 |
-
// 'number_format' => array(),
|
154 |
-
// 'reset_number_yearly' => '',
|
155 |
-
// 'my_account_buttons' => '',
|
156 |
-
// 'invoice_number_column' => '',
|
157 |
-
// 'disable_free' => '',
|
158 |
-
),
|
159 |
-
'wpo_wcpdf_documents_settings_packing-slip' => array(
|
160 |
-
'enabled' => 1,
|
161 |
-
// 'display_billing_address' => '',
|
162 |
-
// 'display_email' => '',
|
163 |
-
// 'display_phone' => '',
|
164 |
-
),
|
165 |
-
// 'wpo_wcpdf_settings_debug' => array(
|
166 |
-
// 'legacy_mode' => '',
|
167 |
-
// 'enable_debug' => '',
|
168 |
-
// 'html_output' => '',
|
169 |
-
// ),
|
170 |
-
);
|
171 |
-
foreach ($settings_defaults as $option => $defaults) {
|
172 |
-
update_option( $option, $defaults );
|
173 |
-
}
|
174 |
-
}
|
175 |
-
|
176 |
-
/**
|
177 |
-
* Plugin upgrade method. Perform any required upgrades here
|
178 |
-
*
|
179 |
-
* @param string $installed_version the currently installed ('old') version
|
180 |
-
*/
|
181 |
-
protected function upgrade( $installed_version ) {
|
182 |
-
// only upgrade when php 5.3 or higher
|
183 |
-
if ( version_compare( PHP_VERSION, '5.3', '<' ) ) {
|
184 |
-
return;
|
185 |
-
}
|
186 |
-
|
187 |
-
// sync fonts on every upgrade!
|
188 |
-
$tmp_base = WPO_WCPDF()->main->get_tmp_base();
|
189 |
-
|
190 |
-
// check if tmp folder exists => if not, initialize
|
191 |
-
if ( !@is_dir( $tmp_base ) ) {
|
192 |
-
WPO_WCPDF()->main->init_tmp( $tmp_base );
|
193 |
-
} else {
|
194 |
-
$font_path = WPO_WCPDF()->main->get_tmp_path( 'fonts' );
|
195 |
-
// don't try merging fonts with local when updating pre 2.0
|
196 |
-
$pre_2 = ( $installed_version == 'versionless' || version_compare( $installed_version, '2.0-dev', '<' ) );
|
197 |
-
$merge_with_local = $pre_2 ? false : true;
|
198 |
-
WPO_WCPDF()->main->copy_fonts( $font_path, $merge_with_local );
|
199 |
-
}
|
200 |
-
|
201 |
-
// 1.5.28 update: copy next invoice number to separate setting
|
202 |
-
if ( $installed_version == 'versionless' || version_compare( $installed_version, '1.5.28', '<' ) ) {
|
203 |
-
$template_settings = get_option( 'wpo_wcpdf_template_settings' );
|
204 |
-
$next_invoice_number = isset($template_settings['next_invoice_number'])?$template_settings['next_invoice_number']:'';
|
205 |
-
update_option( 'wpo_wcpdf_next_invoice_number', $next_invoice_number );
|
206 |
-
}
|
207 |
-
|
208 |
-
// 2.0-dev update: reorganize settings
|
209 |
-
if ( $installed_version == 'versionless' || version_compare( $installed_version, '2.0-dev', '<' ) ) {
|
210 |
-
$old_settings = array(
|
211 |
-
'wpo_wcpdf_general_settings' => get_option( 'wpo_wcpdf_general_settings' ),
|
212 |
-
'wpo_wcpdf_template_settings' => get_option( 'wpo_wcpdf_template_settings' ),
|
213 |
-
'wpo_wcpdf_debug_settings' => get_option( 'wpo_wcpdf_debug_settings' ),
|
214 |
-
);
|
215 |
-
|
216 |
-
// combine invoice number formatting in array
|
217 |
-
$old_settings['wpo_wcpdf_template_settings']['invoice_number_formatting'] = array();
|
218 |
-
$format_option_keys = array('padding','suffix','prefix');
|
219 |
-
foreach ($format_option_keys as $format_option_key) {
|
220 |
-
if (isset($old_settings['wpo_wcpdf_template_settings']["invoice_number_formatting_{$format_option_key}"])) {
|
221 |
-
$old_settings['wpo_wcpdf_template_settings']['invoice_number_formatting'][$format_option_key] = $old_settings['wpo_wcpdf_template_settings']["invoice_number_formatting_{$format_option_key}"];
|
222 |
-
}
|
223 |
-
}
|
224 |
-
|
225 |
-
// convert abbreviated email_ids
|
226 |
-
if (isset($old_settings['wpo_wcpdf_general_settings']['email_pdf'])) {
|
227 |
-
foreach ($old_settings['wpo_wcpdf_general_settings']['email_pdf'] as $email_id => $value) {
|
228 |
-
if ($email_id == 'completed' || $email_id == 'processing') {
|
229 |
-
$old_settings['wpo_wcpdf_general_settings']['email_pdf']["customer_{$email_id}_order"] = $value;
|
230 |
-
unset($old_settings['wpo_wcpdf_general_settings']['email_pdf'][$email_id]);
|
231 |
-
}
|
232 |
-
}
|
233 |
-
}
|
234 |
-
|
235 |
-
// Migrate template path
|
236 |
-
// forward slash for consistency/compatibility
|
237 |
-
$template_path = str_replace('\\','/', $old_settings['wpo_wcpdf_template_settings']['template_path']);
|
238 |
-
// strip abspath (forward slashed) if included
|
239 |
-
$template_path = str_replace( str_replace('\\','/', ABSPATH), '', $template_path );
|
240 |
-
// strip pdf subfolder from templates path
|
241 |
-
$template_path = str_replace( '/templates/pdf/', '/templates/', $template_path );
|
242 |
-
$old_settings['wpo_wcpdf_template_settings']['template_path'] = $template_path;
|
243 |
-
|
244 |
-
// map new settings to old
|
245 |
-
$settings_map = array(
|
246 |
-
'wpo_wcpdf_settings_general' => array(
|
247 |
-
'download_display' => array( 'wpo_wcpdf_general_settings' => 'download_display' ),
|
248 |
-
'template_path' => array( 'wpo_wcpdf_template_settings' => 'template_path' ),
|
249 |
-
'currency_font' => array( 'wpo_wcpdf_template_settings' => 'currency_font' ),
|
250 |
-
'paper_size' => array( 'wpo_wcpdf_template_settings' => 'paper_size' ),
|
251 |
-
'header_logo' => array( 'wpo_wcpdf_template_settings' => 'header_logo' ),
|
252 |
-
'shop_name' => array( 'wpo_wcpdf_template_settings' => 'shop_name' ),
|
253 |
-
'shop_address' => array( 'wpo_wcpdf_template_settings' => 'shop_address' ),
|
254 |
-
'footer' => array( 'wpo_wcpdf_template_settings' => 'footer' ),
|
255 |
-
'extra_1' => array( 'wpo_wcpdf_template_settings' => 'extra_1' ),
|
256 |
-
'extra_2' => array( 'wpo_wcpdf_template_settings' => 'extra_2' ),
|
257 |
-
'extra_3' => array( 'wpo_wcpdf_template_settings' => 'extra_3' ),
|
258 |
-
),
|
259 |
-
'wpo_wcpdf_documents_settings_invoice' => array(
|
260 |
-
'attach_to_email_ids' => array( 'wpo_wcpdf_general_settings' => 'email_pdf' ),
|
261 |
-
'display_shipping_address' => array( 'wpo_wcpdf_template_settings' => 'invoice_shipping_address' ),
|
262 |
-
'display_email' => array( 'wpo_wcpdf_template_settings' => 'invoice_email' ),
|
263 |
-
'display_phone' => array( 'wpo_wcpdf_template_settings' => 'invoice_phone' ),
|
264 |
-
'display_date' => array( 'wpo_wcpdf_template_settings' => 'display_date' ),
|
265 |
-
'display_number' => array( 'wpo_wcpdf_template_settings' => 'display_number' ),
|
266 |
-
'number_format' => array( 'wpo_wcpdf_template_settings' => 'invoice_number_formatting' ),
|
267 |
-
'reset_number_yearly' => array( 'wpo_wcpdf_template_settings' => 'yearly_reset_invoice_number' ),
|
268 |
-
'my_account_buttons' => array( 'wpo_wcpdf_general_settings' => 'my_account_buttons' ),
|
269 |
-
'invoice_number_column' => array( 'wpo_wcpdf_general_settings' => 'invoice_number_column' ),
|
270 |
-
'disable_free' => array( 'wpo_wcpdf_general_settings' => 'disable_free' ),
|
271 |
-
),
|
272 |
-
'wpo_wcpdf_documents_settings_packing-slip' => array(
|
273 |
-
'display_billing_address' => array( 'wpo_wcpdf_template_settings' => 'packing_slip_billing_address' ),
|
274 |
-
'display_email' => array( 'wpo_wcpdf_template_settings' => 'packing_slip_email' ),
|
275 |
-
'display_phone' => array( 'wpo_wcpdf_template_settings' => 'packing_slip_phone' ),
|
276 |
-
),
|
277 |
-
'wpo_wcpdf_settings_debug' => array(
|
278 |
-
'enable_debug' => array( 'wpo_wcpdf_debug_settings' => 'enable_debug' ),
|
279 |
-
'html_output' => array( 'wpo_wcpdf_debug_settings' => 'html_output' ),
|
280 |
-
),
|
281 |
-
);
|
282 |
-
|
283 |
-
// walk through map
|
284 |
-
foreach ($settings_map as $new_option => $new_settings_keys) {
|
285 |
-
${$new_option} = array();
|
286 |
-
foreach ($new_settings_keys as $new_key => $old_setting ) {
|
287 |
-
$old_key = reset($old_setting);
|
288 |
-
$old_option = key($old_setting);
|
289 |
-
if (!empty($old_settings[$old_option][$old_key])) {
|
290 |
-
// turn translatable fields into array
|
291 |
-
$translatable_fields = array('shop_name','shop_address','footer','extra_1','extra_2','extra_3');
|
292 |
-
if (in_array($new_key, $translatable_fields)) {
|
293 |
-
${$new_option}[$new_key] = array( 'default' => $old_settings[$old_option][$old_key] );
|
294 |
-
} else {
|
295 |
-
${$new_option}[$new_key] = $old_settings[$old_option][$old_key];
|
296 |
-
}
|
297 |
-
}
|
298 |
-
}
|
299 |
-
|
300 |
-
// auto enable invoice & packing slip
|
301 |
-
$enabled = array( 'wpo_wcpdf_documents_settings_invoice', 'wpo_wcpdf_documents_settings_packing-slip' );
|
302 |
-
if ( in_array( $new_option, $enabled ) ) {
|
303 |
-
${$new_option}['enabled'] = 1;
|
304 |
-
}
|
305 |
-
|
306 |
-
// auto enable legacy mode
|
307 |
-
if ( $new_option == 'wpo_wcpdf_settings_debug' ) {
|
308 |
-
${$new_option}['legacy_mode'] = 1;
|
309 |
-
}
|
310 |
-
|
311 |
-
// merge with existing settings
|
312 |
-
${$new_option."_old"} = get_option( $new_option, ${$new_option} ); // second argument loads new as default in case the settings did not exist yet
|
313 |
-
${$new_option} = (array) ${$new_option} + (array) ${$new_option."_old"}; // duplicate options take new options as default
|
314 |
-
|
315 |
-
// store new option values
|
316 |
-
update_option( $new_option, ${$new_option} );
|
317 |
-
}
|
318 |
-
}
|
319 |
-
|
320 |
-
// 2.0-beta-2 update: copy next number to separate db store
|
321 |
-
if ( version_compare( $installed_version, '2.0-beta-2', '<' ) ) {
|
322 |
-
// load number store class (just in case)
|
323 |
-
include_once( WPO_WCPDF()->plugin_path() . '/includes/documents/class-wcpdf-sequential-number-store.php' );
|
324 |
-
|
325 |
-
$next_number = get_option( 'wpo_wcpdf_next_invoice_number' );
|
326 |
-
if (!empty($next_number)) {
|
327 |
-
$number_store = new \WPO\WC\PDF_Invoices\Documents\Sequential_Number_Store( 'invoice_number' );
|
328 |
-
$number_store->set_next( (int) $next_number );
|
329 |
-
}
|
330 |
-
// we're not deleting this option yet to make downgrading possible
|
331 |
-
// delete_option( 'wpo_wcpdf_next_invoice_number' ); // clean up after ourselves
|
332 |
-
}
|
333 |
-
|
334 |
-
}
|
335 |
-
|
336 |
-
/**
|
337 |
-
* Plugin downgrade method. Perform any required downgrades here
|
338 |
-
*
|
339 |
-
*
|
340 |
-
* @param string $installed_version the currently installed ('old') version (actually higher since this is a downgrade)
|
341 |
-
*/
|
342 |
-
protected function downgrade( $installed_version ) {
|
343 |
-
// make sure fonts match with version: copy from plugin folder
|
344 |
-
$tmp_base = WPO_WCPDF()->main->get_tmp_base();
|
345 |
-
|
346 |
-
// check if tmp folder exists => if not, initialize
|
347 |
-
if ( !@is_dir( $tmp_base ) ) {
|
348 |
-
WPO_WCPDF()->main->init_tmp( $tmp_base );
|
349 |
-
} else {
|
350 |
-
$font_path = WPO_WCPDF()->main->get_tmp_path( 'fonts' );
|
351 |
-
WPO_WCPDF()->main->copy_fonts( $font_path );
|
352 |
-
}
|
353 |
-
}
|
354 |
-
|
355 |
-
}
|
356 |
-
|
357 |
-
endif; // class_exists
|
358 |
-
|
359 |
return new Install();
|
1 |
+
<?php
|
2 |
+
namespace WPO\WC\PDF_Invoices;
|
3 |
+
|
4 |
+
use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
|
5 |
+
use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
|
6 |
+
use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
|
7 |
+
|
8 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
9 |
+
exit; // Exit if accessed directly
|
10 |
+
}
|
11 |
+
|
12 |
+
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Install' ) ) :
|
13 |
+
|
14 |
+
class Install {
|
15 |
+
|
16 |
+
function __construct() {
|
17 |
+
// run lifecycle methods
|
18 |
+
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
|
19 |
+
add_action( 'wp_loaded', array( $this, 'do_install' ) );
|
20 |
+
}
|
21 |
+
}
|
22 |
+
|
23 |
+
/** Lifecycle methods *******************************************************
|
24 |
+
* Because register_activation_hook only runs when the plugin is manually
|
25 |
+
* activated by the user, we're checking the current version against the
|
26 |
+
* version stored in the database
|
27 |
+
****************************************************************************/
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Handles version checking
|
31 |
+
*/
|
32 |
+
public function do_install() {
|
33 |
+
// only install when woocommerce is active
|
34 |
+
if ( !WPO_WCPDF()->is_woocommerce_activated() ) {
|
35 |
+
return;
|
36 |
+
}
|
37 |
+
|
38 |
+
$version_setting = 'wpo_wcpdf_version';
|
39 |
+
$installed_version = get_option( $version_setting );
|
40 |
+
|
41 |
+
// installed version lower than plugin version?
|
42 |
+
if ( version_compare( $installed_version, WPO_WCPDF_VERSION, '<' ) ) {
|
43 |
+
|
44 |
+
if ( ! $installed_version ) {
|
45 |
+
$this->install();
|
46 |
+
} else {
|
47 |
+
$this->upgrade( $installed_version );
|
48 |
+
}
|
49 |
+
|
50 |
+
// new version number
|
51 |
+
update_option( $version_setting, WPO_WCPDF_VERSION );
|
52 |
+
} elseif ( $installed_version && version_compare( $installed_version, WPO_WCPDF_VERSION, '>' ) ) {
|
53 |
+
$this->downgrade( $installed_version );
|
54 |
+
// downgrade version number
|
55 |
+
update_option( $version_setting, WPO_WCPDF_VERSION );
|
56 |
+
}
|
57 |
+
}
|
58 |
+
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Plugin install method. Perform any installation tasks here
|
62 |
+
*/
|
63 |
+
protected function install() {
|
64 |
+
// only install when php 5.3 or higher
|
65 |
+
if ( version_compare( PHP_VERSION, '5.3', '<' ) ) {
|
66 |
+
return;
|
67 |
+
}
|
68 |
+
|
69 |
+
// check if upgrading from versionless (1.4.14 and older)
|
70 |
+
if ( get_option('wpo_wcpdf_general_settings') ) {
|
71 |
+
$this->upgrade( 'versionless' );
|
72 |
+
return;
|
73 |
+
}
|
74 |
+
|
75 |
+
// Create temp folders
|
76 |
+
$tmp_base = WPO_WCPDF()->main->get_tmp_base();
|
77 |
+
|
78 |
+
// check if tmp folder exists => if not, initialize
|
79 |
+
if ( !@is_dir( $tmp_base ) ) {
|
80 |
+
WPO_WCPDF()->main->init_tmp( $tmp_base );
|
81 |
+
}
|
82 |
+
|
83 |
+
// Unsupported currency symbols
|
84 |
+
$unsupported_symbols = array (
|
85 |
+
'AED',
|
86 |
+
'AFN',
|
87 |
+
'BDT',
|
88 |
+
'BHD',
|
89 |
+
'BTC',
|
90 |
+
'CRC',
|
91 |
+
'DZD',
|
92 |
+
'GEL',
|
93 |
+
'GHS',
|
94 |
+
'ILS',
|
95 |
+
'INR',
|
96 |
+
'IQD',
|
97 |
+
'IRR',
|
98 |
+
'IRT',
|
99 |
+
'JOD',
|
100 |
+
'KHR',
|
101 |
+
'KPW',
|
102 |
+
'KRW',
|
103 |
+
'KWD',
|
104 |
+
'LAK',
|
105 |
+
'LBP',
|
106 |
+
'LKR',
|
107 |
+
'LYD',
|
108 |
+
'MAD',
|
109 |
+
'MNT',
|
110 |
+
'MUR',
|
111 |
+
'MVR',
|
112 |
+
'NPR',
|
113 |
+
'OMR',
|
114 |
+
'PHP',
|
115 |
+
'PKR',
|
116 |
+
'PYG',
|
117 |
+
'QAR',
|
118 |
+
'RUB',
|
119 |
+
'SAR',
|
120 |
+
'SCR',
|
121 |
+
'SDG',
|
122 |
+
'SYP',
|
123 |
+
'THB',
|
124 |
+
'TND',
|
125 |
+
'TRY',
|
126 |
+
'UAH',
|
127 |
+
'YER',
|
128 |
+
);
|
129 |
+
|
130 |
+
// set default settings
|
131 |
+
$settings_defaults = array(
|
132 |
+
'wpo_wcpdf_settings_general' => array(
|
133 |
+
'download_display' => 'display',
|
134 |
+
'template_path' => WPO_WCPDF()->plugin_path() . '/templates/Simple',
|
135 |
+
'currency_font' => ( in_array( get_woocommerce_currency(), $unsupported_symbols ) ) ? 1 : '',
|
136 |
+
'paper_size' => 'a4',
|
137 |
+
// 'header_logo' => '',
|
138 |
+
// 'shop_name' => array(),
|
139 |
+
// 'shop_address' => array(),
|
140 |
+
// 'footer' => array(),
|
141 |
+
// 'extra_1' => array(),
|
142 |
+
// 'extra_2' => array(),
|
143 |
+
// 'extra_3' => array(),
|
144 |
+
),
|
145 |
+
'wpo_wcpdf_documents_settings_invoice' => array(
|
146 |
+
'enabled' => 1,
|
147 |
+
// 'attach_to_email_ids' => array(),
|
148 |
+
// 'display_shipping_address' => '',
|
149 |
+
// 'display_email' => '',
|
150 |
+
// 'display_phone' => '',
|
151 |
+
// 'display_date' => '',
|
152 |
+
// 'display_number' => '',
|
153 |
+
// 'number_format' => array(),
|
154 |
+
// 'reset_number_yearly' => '',
|
155 |
+
// 'my_account_buttons' => '',
|
156 |
+
// 'invoice_number_column' => '',
|
157 |
+
// 'disable_free' => '',
|
158 |
+
),
|
159 |
+
'wpo_wcpdf_documents_settings_packing-slip' => array(
|
160 |
+
'enabled' => 1,
|
161 |
+
// 'display_billing_address' => '',
|
162 |
+
// 'display_email' => '',
|
163 |
+
// 'display_phone' => '',
|
164 |
+
),
|
165 |
+
// 'wpo_wcpdf_settings_debug' => array(
|
166 |
+
// 'legacy_mode' => '',
|
167 |
+
// 'enable_debug' => '',
|
168 |
+
// 'html_output' => '',
|
169 |
+
// ),
|
170 |
+
);
|
171 |
+
foreach ($settings_defaults as $option => $defaults) {
|
172 |
+
update_option( $option, $defaults );
|
173 |
+
}
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* Plugin upgrade method. Perform any required upgrades here
|
178 |
+
*
|
179 |
+
* @param string $installed_version the currently installed ('old') version
|
180 |
+
*/
|
181 |
+
protected function upgrade( $installed_version ) {
|
182 |
+
// only upgrade when php 5.3 or higher
|
183 |
+
if ( version_compare( PHP_VERSION, '5.3', '<' ) ) {
|
184 |
+
return;
|
185 |
+
}
|
186 |
+
|
187 |
+
// sync fonts on every upgrade!
|
188 |
+
$tmp_base = WPO_WCPDF()->main->get_tmp_base();
|
189 |
+
|
190 |
+
// check if tmp folder exists => if not, initialize
|
191 |
+
if ( !@is_dir( $tmp_base ) ) {
|
192 |
+
WPO_WCPDF()->main->init_tmp( $tmp_base );
|
193 |
+
} else {
|
194 |
+
$font_path = WPO_WCPDF()->main->get_tmp_path( 'fonts' );
|
195 |
+
// don't try merging fonts with local when updating pre 2.0
|
196 |
+
$pre_2 = ( $installed_version == 'versionless' || version_compare( $installed_version, '2.0-dev', '<' ) );
|
197 |
+
$merge_with_local = $pre_2 ? false : true;
|
198 |
+
WPO_WCPDF()->main->copy_fonts( $font_path, $merge_with_local );
|
199 |
+
}
|
200 |
+
|
201 |
+
// 1.5.28 update: copy next invoice number to separate setting
|
202 |
+
if ( $installed_version == 'versionless' || version_compare( $installed_version, '1.5.28', '<' ) ) {
|
203 |
+
$template_settings = get_option( 'wpo_wcpdf_template_settings' );
|
204 |
+
$next_invoice_number = isset($template_settings['next_invoice_number'])?$template_settings['next_invoice_number']:'';
|
205 |
+
update_option( 'wpo_wcpdf_next_invoice_number', $next_invoice_number );
|
206 |
+
}
|
207 |
+
|
208 |
+
// 2.0-dev update: reorganize settings
|
209 |
+
if ( $installed_version == 'versionless' || version_compare( $installed_version, '2.0-dev', '<' ) ) {
|
210 |
+
$old_settings = array(
|
211 |
+
'wpo_wcpdf_general_settings' => get_option( 'wpo_wcpdf_general_settings' ),
|
212 |
+
'wpo_wcpdf_template_settings' => get_option( 'wpo_wcpdf_template_settings' ),
|
213 |
+
'wpo_wcpdf_debug_settings' => get_option( 'wpo_wcpdf_debug_settings' ),
|
214 |
+
);
|
215 |
+
|
216 |
+
// combine invoice number formatting in array
|
217 |
+
$old_settings['wpo_wcpdf_template_settings']['invoice_number_formatting'] = array();
|
218 |
+
$format_option_keys = array('padding','suffix','prefix');
|
219 |
+
foreach ($format_option_keys as $format_option_key) {
|
220 |
+
if (isset($old_settings['wpo_wcpdf_template_settings']["invoice_number_formatting_{$format_option_key}"])) {
|
221 |
+
$old_settings['wpo_wcpdf_template_settings']['invoice_number_formatting'][$format_option_key] = $old_settings['wpo_wcpdf_template_settings']["invoice_number_formatting_{$format_option_key}"];
|
222 |
+
}
|
223 |
+
}
|
224 |
+
|
225 |
+
// convert abbreviated email_ids
|
226 |
+
if (isset($old_settings['wpo_wcpdf_general_settings']['email_pdf'])) {
|
227 |
+
foreach ($old_settings['wpo_wcpdf_general_settings']['email_pdf'] as $email_id => $value) {
|
228 |
+
if ($email_id == 'completed' || $email_id == 'processing') {
|
229 |
+
$old_settings['wpo_wcpdf_general_settings']['email_pdf']["customer_{$email_id}_order"] = $value;
|
230 |
+
unset($old_settings['wpo_wcpdf_general_settings']['email_pdf'][$email_id]);
|
231 |
+
}
|
232 |
+
}
|
233 |
+
}
|
234 |
+
|
235 |
+
// Migrate template path
|
236 |
+
// forward slash for consistency/compatibility
|
237 |
+
$template_path = str_replace('\\','/', $old_settings['wpo_wcpdf_template_settings']['template_path']);
|
238 |
+
// strip abspath (forward slashed) if included
|
239 |
+
$template_path = str_replace( str_replace('\\','/', ABSPATH), '', $template_path );
|
240 |
+
// strip pdf subfolder from templates path
|
241 |
+
$template_path = str_replace( '/templates/pdf/', '/templates/', $template_path );
|
242 |
+
$old_settings['wpo_wcpdf_template_settings']['template_path'] = $template_path;
|
243 |
+
|
244 |
+
// map new settings to old
|
245 |
+
$settings_map = array(
|
246 |
+
'wpo_wcpdf_settings_general' => array(
|
247 |
+
'download_display' => array( 'wpo_wcpdf_general_settings' => 'download_display' ),
|
248 |
+
'template_path' => array( 'wpo_wcpdf_template_settings' => 'template_path' ),
|
249 |
+
'currency_font' => array( 'wpo_wcpdf_template_settings' => 'currency_font' ),
|
250 |
+
'paper_size' => array( 'wpo_wcpdf_template_settings' => 'paper_size' ),
|
251 |
+
'header_logo' => array( 'wpo_wcpdf_template_settings' => 'header_logo' ),
|
252 |
+
'shop_name' => array( 'wpo_wcpdf_template_settings' => 'shop_name' ),
|
253 |
+
'shop_address' => array( 'wpo_wcpdf_template_settings' => 'shop_address' ),
|
254 |
+
'footer' => array( 'wpo_wcpdf_template_settings' => 'footer' ),
|
255 |
+
'extra_1' => array( 'wpo_wcpdf_template_settings' => 'extra_1' ),
|
256 |
+
'extra_2' => array( 'wpo_wcpdf_template_settings' => 'extra_2' ),
|
257 |
+
'extra_3' => array( 'wpo_wcpdf_template_settings' => 'extra_3' ),
|
258 |
+
),
|
259 |
+
'wpo_wcpdf_documents_settings_invoice' => array(
|
260 |
+
'attach_to_email_ids' => array( 'wpo_wcpdf_general_settings' => 'email_pdf' ),
|
261 |
+
'display_shipping_address' => array( 'wpo_wcpdf_template_settings' => 'invoice_shipping_address' ),
|
262 |
+
'display_email' => array( 'wpo_wcpdf_template_settings' => 'invoice_email' ),
|
263 |
+
'display_phone' => array( 'wpo_wcpdf_template_settings' => 'invoice_phone' ),
|
264 |
+
'display_date' => array( 'wpo_wcpdf_template_settings' => 'display_date' ),
|
265 |
+
'display_number' => array( 'wpo_wcpdf_template_settings' => 'display_number' ),
|
266 |
+
'number_format' => array( 'wpo_wcpdf_template_settings' => 'invoice_number_formatting' ),
|
267 |
+
'reset_number_yearly' => array( 'wpo_wcpdf_template_settings' => 'yearly_reset_invoice_number' ),
|
268 |
+
'my_account_buttons' => array( 'wpo_wcpdf_general_settings' => 'my_account_buttons' ),
|
269 |
+
'invoice_number_column' => array( 'wpo_wcpdf_general_settings' => 'invoice_number_column' ),
|
270 |
+
'disable_free' => array( 'wpo_wcpdf_general_settings' => 'disable_free' ),
|
271 |
+
),
|
272 |
+
'wpo_wcpdf_documents_settings_packing-slip' => array(
|
273 |
+
'display_billing_address' => array( 'wpo_wcpdf_template_settings' => 'packing_slip_billing_address' ),
|
274 |
+
'display_email' => array( 'wpo_wcpdf_template_settings' => 'packing_slip_email' ),
|
275 |
+
'display_phone' => array( 'wpo_wcpdf_template_settings' => 'packing_slip_phone' ),
|
276 |
+
),
|
277 |
+
'wpo_wcpdf_settings_debug' => array(
|
278 |
+
'enable_debug' => array( 'wpo_wcpdf_debug_settings' => 'enable_debug' ),
|
279 |
+
'html_output' => array( 'wpo_wcpdf_debug_settings' => 'html_output' ),
|
280 |
+
),
|
281 |
+
);
|
282 |
+
|
283 |
+
// walk through map
|
284 |
+
foreach ($settings_map as $new_option => $new_settings_keys) {
|
285 |
+
${$new_option} = array();
|
286 |
+
foreach ($new_settings_keys as $new_key => $old_setting ) {
|
287 |
+
$old_key = reset($old_setting);
|
288 |
+
$old_option = key($old_setting);
|
289 |
+
if (!empty($old_settings[$old_option][$old_key])) {
|
290 |
+
// turn translatable fields into array
|
291 |
+
$translatable_fields = array('shop_name','shop_address','footer','extra_1','extra_2','extra_3');
|
292 |
+
if (in_array($new_key, $translatable_fields)) {
|
293 |
+
${$new_option}[$new_key] = array( 'default' => $old_settings[$old_option][$old_key] );
|
294 |
+
} else {
|
295 |
+
${$new_option}[$new_key] = $old_settings[$old_option][$old_key];
|
296 |
+
}
|
297 |
+
}
|
298 |
+
}
|
299 |
+
|
300 |
+
// auto enable invoice & packing slip
|
301 |
+
$enabled = array( 'wpo_wcpdf_documents_settings_invoice', 'wpo_wcpdf_documents_settings_packing-slip' );
|
302 |
+
if ( in_array( $new_option, $enabled ) ) {
|
303 |
+
${$new_option}['enabled'] = 1;
|
304 |
+
}
|
305 |
+
|
306 |
+
// auto enable legacy mode
|
307 |
+
if ( $new_option == 'wpo_wcpdf_settings_debug' ) {
|
308 |
+
${$new_option}['legacy_mode'] = 1;
|
309 |
+
}
|
310 |
+
|
311 |
+
// merge with existing settings
|
312 |
+
${$new_option."_old"} = get_option( $new_option, ${$new_option} ); // second argument loads new as default in case the settings did not exist yet
|
313 |
+
${$new_option} = (array) ${$new_option} + (array) ${$new_option."_old"}; // duplicate options take new options as default
|
314 |
+
|
315 |
+
// store new option values
|
316 |
+
update_option( $new_option, ${$new_option} );
|
317 |
+
}
|
318 |
+
}
|
319 |
+
|
320 |
+
// 2.0-beta-2 update: copy next number to separate db store
|
321 |
+
if ( version_compare( $installed_version, '2.0-beta-2', '<' ) ) {
|
322 |
+
// load number store class (just in case)
|
323 |
+
include_once( WPO_WCPDF()->plugin_path() . '/includes/documents/class-wcpdf-sequential-number-store.php' );
|
324 |
+
|
325 |
+
$next_number = get_option( 'wpo_wcpdf_next_invoice_number' );
|
326 |
+
if (!empty($next_number)) {
|
327 |
+
$number_store = new \WPO\WC\PDF_Invoices\Documents\Sequential_Number_Store( 'invoice_number' );
|
328 |
+
$number_store->set_next( (int) $next_number );
|
329 |
+
}
|
330 |
+
// we're not deleting this option yet to make downgrading possible
|
331 |
+
// delete_option( 'wpo_wcpdf_next_invoice_number' ); // clean up after ourselves
|
332 |
+
}
|
333 |
+
|
334 |
+
}
|
335 |
+
|
336 |
+
/**
|
337 |
+
* Plugin downgrade method. Perform any required downgrades here
|
338 |
+
*
|
339 |
+
*
|
340 |
+
* @param string $installed_version the currently installed ('old') version (actually higher since this is a downgrade)
|
341 |
+
*/
|
342 |
+
protected function downgrade( $installed_version ) {
|
343 |
+
// make sure fonts match with version: copy from plugin folder
|
344 |
+
$tmp_base = WPO_WCPDF()->main->get_tmp_base();
|
345 |
+
|
346 |
+
// check if tmp folder exists => if not, initialize
|
347 |
+
if ( !@is_dir( $tmp_base ) ) {
|
348 |
+
WPO_WCPDF()->main->init_tmp( $tmp_base );
|
349 |
+
} else {
|
350 |
+
$font_path = WPO_WCPDF()->main->get_tmp_path( 'fonts' );
|
351 |
+
WPO_WCPDF()->main->copy_fonts( $font_path );
|
352 |
+
}
|
353 |
+
}
|
354 |
+
|
355 |
+
}
|
356 |
+
|
357 |
+
endif; // class_exists
|
358 |
+
|
359 |
return new Install();
|
includes/class-wcpdf-main.php
CHANGED
@@ -1,502 +1,502 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPO\WC\PDF_Invoices;
|
3 |
-
|
4 |
-
use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
|
5 |
-
use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
|
6 |
-
use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
|
7 |
-
|
8 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
9 |
-
exit; // Exit if accessed directly
|
10 |
-
}
|
11 |
-
|
12 |
-
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Main' ) ) :
|
13 |
-
|
14 |
-
class Main {
|
15 |
-
|
16 |
-
function __construct() {
|
17 |
-
add_action( 'wp_ajax_generate_wpo_wcpdf', array($this, 'generate_pdf_ajax' ) );
|
18 |
-
add_filter( 'woocommerce_email_attachments', array( $this, 'attach_pdf_to_email' ), 99, 3 );
|
19 |
-
add_filter( 'wpo_wcpdf_custom_attachment_condition', array( $this, 'disable_free_attachment'), 10, 4 );
|
20 |
-
|
21 |
-
if ( isset(WPO_WCPDF()->settings->debug_settings['enable_debug']) ) {
|
22 |
-
$this->enable_debug();
|
23 |
-
}
|
24 |
-
if ( isset(WPO_WCPDF()->settings->debug_settings['html_output']) ) {
|
25 |
-
add_filter( 'wpo_wcpdf_use_path', '__return_false' );
|
26 |
-
}
|
27 |
-
|
28 |
-
// include template specific custom functions
|
29 |
-
$template_path = WPO_WCPDF()->settings->get_template_path();
|
30 |
-
if ( file_exists( $template_path . '/template-functions.php' ) ) {
|
31 |
-
require_once( $template_path . '/template-functions.php' );
|
32 |
-
}
|
33 |
-
|
34 |
-
// page numbers & currency filters
|
35 |
-
add_action( 'wpo_wcpdf_get_html', array($this, 'format_page_number_placeholders' ), 10, 2 );
|
36 |
-
add_action( 'wpo_wcpdf_after_dompdf_render', array($this, 'page_number_replacements' ), 9, 2 );
|
37 |
-
if ( isset( WPO_WCPDF()->settings->general_settings['currency_font'] ) ) {
|
38 |
-
add_action( 'wpo_wcpdf_before_pdf', array($this, 'use_currency_font' ), 10, 2 );
|
39 |
-
}
|
40 |
-
|
41 |
-
// scheduled attachments cleanup - disabled for now
|
42 |
-
// add_action( 'wp_scheduled_delete', array( $this, 'attachments_cleanup') );
|
43 |
-
}
|
44 |
-
|
45 |
-
/**
|
46 |
-
* Attach PDF to WooCommerce email
|
47 |
-
*/
|
48 |
-
public function attach_pdf_to_email ( $attachments, $email_id, $order ) {
|
49 |
-
// check if all variables properly set
|
50 |
-
if ( !is_object( $order ) || !isset( $email_id ) ) {
|
51 |
-
return $attachments;
|
52 |
-
}
|
53 |
-
|
54 |
-
// Skip User emails
|
55 |
-
if ( get_class( $order ) == 'WP_User' ) {
|
56 |
-
return $attachments;
|
57 |
-
}
|
58 |
-
|
59 |
-
$order_id = WCX_Order::get_id( $order );
|
60 |
-
|
61 |
-
if ( get_class( $order ) !== 'WC_Order' && $order_id == false ) {
|
62 |
-
return $attachments;
|
63 |
-
}
|
64 |
-
|
65 |
-
// WooCommerce Booking compatibility
|
66 |
-
if ( get_post_type( $order_id ) == 'wc_booking' && isset($order->order) ) {
|
67 |
-
// $order is actually a WC_Booking object!
|
68 |
-
$order = $order->order;
|
69 |
-
}
|
70 |
-
|
71 |
-
// do not process low stock notifications, user emails etc!
|
72 |
-
if ( in_array( $email_id, array( 'no_stock', 'low_stock', 'backorder', 'customer_new_account', 'customer_reset_password' ) ) || get_post_type( $order_id ) != 'shop_order' ) {
|
73 |
-
return $attachments;
|
74 |
-
}
|
75 |
-
|
76 |
-
$tmp_path = $this->get_tmp_path('attachments');
|
77 |
-
|
78 |
-
// clear pdf files from temp folder (from http://stackoverflow.com/a/13468943/1446634)
|
79 |
-
// array_map('unlink', ( glob( $tmp_path.'*.pdf' ) ? glob( $tmp_path.'*.pdf' ) : array() ) );
|
80 |
-
|
81 |
-
// disable deprecation notices during email sending
|
82 |
-
add_filter( 'wcpdf_disable_deprecation_notices', '__return_true' );
|
83 |
-
|
84 |
-
$attach_to_document_types = $this->get_documents_for_email( $email_id, $order );
|
85 |
-
foreach ( $attach_to_document_types as $document_type ) {
|
86 |
-
do_action( 'wpo_wcpdf_before_attachment_creation', $order, $email_id, $document_type );
|
87 |
-
|
88 |
-
try {
|
89 |
-
// prepare document
|
90 |
-
$document = wcpdf_get_document( $document_type, (array) $order_id, true );
|
91 |
-
if ( !$document ) { // something went wrong, continue trying with other documents
|
92 |
-
continue;
|
93 |
-
}
|
94 |
-
|
95 |
-
// get pdf data & store
|
96 |
-
$pdf_data = $document->get_pdf();
|
97 |
-
$filename = $document->get_filename();
|
98 |
-
$pdf_path = $tmp_path . $filename;
|
99 |
-
file_put_contents ( $pdf_path, $pdf_data );
|
100 |
-
$attachments[] = $pdf_path;
|
101 |
-
|
102 |
-
do_action( 'wpo_wcpdf_email_attachment', $pdf_path, $document_type );
|
103 |
-
} catch (Exception $e) {
|
104 |
-
error_log($e->getMessage());
|
105 |
-
continue;
|
106 |
-
}
|
107 |
-
}
|
108 |
-
|
109 |
-
remove_filter( 'wcpdf_disable_deprecation_notices', '__return_true' );
|
110 |
-
|
111 |
-
return $attachments;
|
112 |
-
}
|
113 |
-
|
114 |
-
public function get_documents_for_email( $email_id, $order ) {
|
115 |
-
$documents = WPO_WCPDF()->documents->get_documents();
|
116 |
-
|
117 |
-
$attach_documents = array();
|
118 |
-
foreach ($documents as $document) {
|
119 |
-
$attach_documents[ $document->get_type() ] = $document->get_attach_to_email_ids();
|
120 |
-
}
|
121 |
-
$attach_documents = apply_filters('wpo_wcpdf_attach_documents', $attach_documents );
|
122 |
-
|
123 |
-
$document_types = array();
|
124 |
-
foreach ($attach_documents as $document_type => $attach_to_email_ids ) {
|
125 |
-
// legacy settings: convert abbreviated email_ids
|
126 |
-
foreach ($attach_to_email_ids as $key => $attach_to_email_id) {
|
127 |
-
if ($attach_to_email_id == 'completed' || $attach_to_email_id == 'processing') {
|
128 |
-
$attach_to_email_ids[$key] = "customer_" . $attach_to_email_id . "_order";
|
129 |
-
}
|
130 |
-
}
|
131 |
-
|
132 |
-
$extra_condition = apply_filters('wpo_wcpdf_custom_attachment_condition', true, $order, $email_id, $document_type );
|
133 |
-
if ( in_array( $email_id, $attach_to_email_ids ) && $extra_condition === true ) {
|
134 |
-
$document_types[] = $document_type;
|
135 |
-
}
|
136 |
-
}
|
137 |
-
|
138 |
-
return $document_types;
|
139 |
-
}
|
140 |
-
|
141 |
-
/**
|
142 |
-
* Load and generate the template output with ajax
|
143 |
-
*/
|
144 |
-
public function generate_pdf_ajax() {
|
145 |
-
// Check the nonce
|
146 |
-
if( empty( $_GET['action'] ) || !check_admin_referer( $_GET['action'] ) ) {
|
147 |
-
wp_die( __( 'You do not have sufficient permissions to access this page.', 'woocommerce-pdf-invoices-packing-slips' ) );
|
148 |
-
}
|
149 |
-
|
150 |
-
// Check if all parameters are set
|
151 |
-
if ( empty( $_GET['document_type'] ) && !empty( $_GET['template_type'] ) ) {
|
152 |
-
$_GET['document_type'] = $_GET['template_type'];
|
153 |
-
}
|
154 |
-
|
155 |
-
if ( empty( $_GET['order_ids'] ) ) {
|
156 |
-
wp_die( __( "You haven't selected any orders", 'woocommerce-pdf-invoices-packing-slips' ) );
|
157 |
-
}
|
158 |
-
|
159 |
-
if( empty( $_GET['document_type'] ) ) {
|
160 |
-
wp_die( __( 'Some of the export parameters are missing.', 'woocommerce-pdf-invoices-packing-slips' ) );
|
161 |
-
}
|
162 |
-
|
163 |
-
// Generate the output
|
164 |
-
$document_type = sanitize_text_field( $_GET['document_type'] );
|
165 |
-
|
166 |
-
$order_ids = (array) array_map( 'absint', explode( 'x', $_GET['order_ids'] ) );
|
167 |
-
// Process oldest first: reverse $order_ids array
|
168 |
-
$order_ids = array_reverse( $order_ids );
|
169 |
-
|
170 |
-
// set default is allowed
|
171 |
-
$allowed = true;
|
172 |
-
|
173 |
-
// check if user is logged in
|
174 |
-
if ( ! is_user_logged_in() ) {
|
175 |
-
$allowed = false;
|
176 |
-
}
|
177 |
-
|
178 |
-
// Check the user privileges
|
179 |
-
if( !( current_user_can( 'manage_woocommerce_orders' ) || current_user_can( 'edit_shop_orders' ) ) && !isset( $_GET['my-account'] ) ) {
|
180 |
-
$allowed = false;
|
181 |
-
}
|
182 |
-
|
183 |
-
// User call from my-account page
|
184 |
-
if ( !current_user_can('manage_options') && isset( $_GET['my-account'] ) ) {
|
185 |
-
// Only for single orders!
|
186 |
-
if ( count( $order_ids ) > 1 ) {
|
187 |
-
$allowed = false;
|
188 |
-
}
|
189 |
-
|
190 |
-
// Check if current user is owner of order IMPORTANT!!!
|
191 |
-
if ( ! current_user_can( 'view_order', $order_ids[0] ) ) {
|
192 |
-
$allowed = false;
|
193 |
-
}
|
194 |
-
}
|
195 |
-
|
196 |
-
$allowed = apply_filters( 'wpo_wcpdf_check_privs', $allowed, $order_ids );
|
197 |
-
|
198 |
-
if ( ! $allowed ) {
|
199 |
-
wp_die( __( 'You do not have sufficient permissions to access this page.', 'woocommerce-pdf-invoices-packing-slips' ) );
|
200 |
-
}
|
201 |
-
|
202 |
-
// if we got here, we're safe to go!
|
203 |
-
try {
|
204 |
-
$document = wcpdf_get_document( $document_type, $order_ids, true );
|
205 |
-
|
206 |
-
if ( $document ) {
|
207 |
-
$output_format = WPO_WCPDF()->settings->get_output_format( $document_type );
|
208 |
-
switch ( $output_format ) {
|
209 |
-
case 'html':
|
210 |
-
$document->output_html();
|
211 |
-
break;
|
212 |
-
case 'pdf':
|
213 |
-
default:
|
214 |
-
if ( has_action( 'wpo_wcpdf_created_manually' ) ) {
|
215 |
-
do_action( 'wpo_wcpdf_created_manually', $document->get_pdf(), $document->get_filename() );
|
216 |
-
}
|
217 |
-
$output_mode = WPO_WCPDF()->settings->get_output_mode( $document_type );
|
218 |
-
$document->output_pdf( $output_mode );
|
219 |
-
break;
|
220 |
-
}
|
221 |
-
} else {
|
222 |
-
wp_die( sprintf( __( "Document of type '%s' for the selected order(s) could not be generated", 'woocommerce-pdf-invoices-packing-slips' ), $document_type ) );
|
223 |
-
}
|
224 |
-
} catch (Exception $e) {
|
225 |
-
echo $e->getMessage();
|
226 |
-
}
|
227 |
-
|
228 |
-
exit;
|
229 |
-
}
|
230 |
-
|
231 |
-
/**
|
232 |
-
* Return tmp path for different plugin processes
|
233 |
-
*/
|
234 |
-
public function get_tmp_path ( $type = '' ) {
|
235 |
-
$tmp_base = $this->get_tmp_base();
|
236 |
-
// check if tmp folder exists => if not, initialize
|
237 |
-
if ( !@is_dir( $tmp_base ) ) {
|
238 |
-
$this->init_tmp( $tmp_base );
|
239 |
-
}
|
240 |
-
|
241 |
-
if ( empty( $type ) ) {
|
242 |
-
return $tmp_base;
|
243 |
-
}
|
244 |
-
|
245 |
-
switch ( $type ) {
|
246 |
-
case 'dompdf':
|
247 |
-
$tmp_path = $tmp_base . 'dompdf';
|
248 |
-
break;
|
249 |
-
case 'font_cache':
|
250 |
-
case 'fonts':
|
251 |
-
$tmp_path = $tmp_base . 'fonts';
|
252 |
-
break;
|
253 |
-
case 'attachments':
|
254 |
-
$tmp_path = $tmp_base . 'attachments/';
|
255 |
-
break;
|
256 |
-
default:
|
257 |
-
$tmp_path = $tmp_base . $type;
|
258 |
-
break;
|
259 |
-
}
|
260 |
-
|
261 |
-
// double check for existence, in case tmp_base was installed, but subfolder not created
|
262 |
-
if ( !@is_dir( $tmp_path ) ) {
|
263 |
-
@mkdir( $tmp_path );
|
264 |
-
}
|
265 |
-
|
266 |
-
return $tmp_path;
|
267 |
-
}
|
268 |
-
|
269 |
-
/**
|
270 |
-
* return the base tmp folder (usually uploads)
|
271 |
-
*/
|
272 |
-
public function get_tmp_base () {
|
273 |
-
// wp_upload_dir() is used to set the base temp folder, under which a
|
274 |
-
// 'wpo_wcpdf' folder and several subfolders are created
|
275 |
-
//
|
276 |
-
// wp_upload_dir() will:
|
277 |
-
// * default to WP_CONTENT_DIR/uploads
|
278 |
-
// * UNLESS the ‘UPLOADS’ constant is defined in wp-config (http://codex.wordpress.org/Editing_wp-config.php#Moving_uploads_folder)
|
279 |
-
//
|
280 |
-
// May also be overridden by the wpo_wcpdf_tmp_path filter
|
281 |
-
|
282 |
-
$upload_dir = wp_upload_dir();
|
283 |
-
$upload_base = trailingslashit( $upload_dir['basedir'] );
|
284 |
-
$tmp_base = trailingslashit( apply_filters( 'wpo_wcpdf_tmp_path', $upload_base . 'wpo_wcpdf/' ) );
|
285 |
-
return $tmp_base;
|
286 |
-
}
|
287 |
-
|
288 |
-
/**
|
289 |
-
* Install/create plugin tmp folders
|
290 |
-
*/
|
291 |
-
public function init_tmp ( $tmp_base ) {
|
292 |
-
// create plugin base temp folder
|
293 |
-
@mkdir( $tmp_base );
|
294 |
-
|
295 |
-
// create subfolders & protect
|
296 |
-
$subfolders = array( 'attachments', 'fonts', 'dompdf' );
|
297 |
-
foreach ( $subfolders as $subfolder ) {
|
298 |
-
$path = $tmp_base . $subfolder . '/';
|
299 |
-
@mkdir( $path );
|
300 |
-
|
301 |
-
// copy font files
|
302 |
-
if ( $subfolder == 'fonts' ) {
|
303 |
-
$this->copy_fonts( $path, false );
|
304 |
-
}
|
305 |
-
|
306 |
-
// create .htaccess file and empty index.php to protect in case an open webfolder is used!
|
307 |
-
@file_put_contents( $path . '.htaccess', 'deny from all' );
|
308 |
-
@touch( $path . 'index.php' );
|
309 |
-
}
|
310 |
-
|
311 |
-
}
|
312 |
-
|
313 |
-
/**
|
314 |
-
* Copy DOMPDF fonts to wordpress tmp folder
|
315 |
-
*/
|
316 |
-
public function copy_fonts ( $path, $merge_with_local = true ) {
|
317 |
-
$path = trailingslashit( $path );
|
318 |
-
$dompdf_font_dir = WPO_WCPDF()->plugin_path() . "/vendor/dompdf/dompdf/lib/fonts/";
|
319 |
-
|
320 |
-
// get local font dir from filtered options
|
321 |
-
$dompdf_options = apply_filters( 'wpo_wcpdf_dompdf_options', array(
|
322 |
-
'defaultFont' => 'dejavu sans',
|
323 |
-
'tempDir' => $this->get_tmp_path('dompdf'),
|
324 |
-
'logOutputFile' => $this->get_tmp_path('dompdf') . "/log.htm",
|
325 |
-
'fontDir' => $this->get_tmp_path('fonts'),
|
326 |
-
'fontCache' => $this->get_tmp_path('fonts'),
|
327 |
-
'isRemoteEnabled' => true,
|
328 |
-
'isFontSubsettingEnabled' => true,
|
329 |
-
'isHtml5ParserEnabled' => true,
|
330 |
-
) );
|
331 |
-
$fontDir = $dompdf_options['fontDir'];
|
332 |
-
|
333 |
-
// merge font family cache with local/custom if present
|
334 |
-
$font_cache_files = array(
|
335 |
-
'cache' => 'dompdf_font_family_cache.php',
|
336 |
-
'cache_dist' => 'dompdf_font_family_cache.dist.php',
|
337 |
-
);
|
338 |
-
foreach ( $font_cache_files as $font_cache_name => $font_cache_filename ) {
|
339 |
-
$plugin_fonts = @require $dompdf_font_dir . $font_cache_filename;
|
340 |
-
if ( $merge_with_local && is_readable( $path . $font_cache_filename ) ) {
|
341 |
-
$local_fonts = @require $path . $font_cache_filename;
|
342 |
-
if (is_array($local_fonts) && is_array($plugin_fonts)) {
|
343 |
-
// merge local & plugin fonts, plugin fonts overwrite (update) local fonts
|
344 |
-
// while custom local fonts are retained
|
345 |
-
$local_fonts = array_merge($local_fonts, $plugin_fonts);
|
346 |
-
// create readable array with $fontDir in place of the actual folder for portability
|
347 |
-
$fonts_export = var_export($local_fonts,true);
|
348 |
-
$fonts_export = str_replace('\'' . $fontDir , '$fontDir . \'', $fonts_export);
|
349 |
-
$cacheData = sprintf("<?php return %s;%s?>", $fonts_export, PHP_EOL );
|
350 |
-
// write file with merged cache data
|
351 |
-
file_put_contents($path . $font_cache_filename, $cacheData);
|
352 |
-
} else { // empty local file
|
353 |
-
copy( $dompdf_font_dir . $font_cache_filename, $path . $font_cache_filename );
|
354 |
-
}
|
355 |
-
} else {
|
356 |
-
// we couldn't read the local font cache file so we're simply copying over plugin cache file
|
357 |
-
copy( $dompdf_font_dir . $font_cache_filename, $path . $font_cache_filename );
|
358 |
-
}
|
359 |
-
}
|
360 |
-
|
361 |
-
// first try the easy way with glob!
|
362 |
-
if ( function_exists('glob') ) {
|
363 |
-
$files = glob($dompdf_font_dir."*.*");
|
364 |
-
foreach($files as $file){
|
365 |
-
$filename = basename($file);
|
366 |
-
if( !is_dir($file) && is_readable($file) && !in_array($filename, $font_cache_files)) {
|
367 |
-
$dest = $path . $filename;
|
368 |
-
copy($file, $dest);
|
369 |
-
}
|
370 |
-
}
|
371 |
-
} else {
|
372 |
-
// fallback method using font cache file (glob is disabled on some servers with disable_functions)
|
373 |
-
$extensions = array( '.ttf', '.ufm', '.ufm.php', '.afm', '.afm.php' );
|
374 |
-
$fontDir = untrailingslashit($dompdf_font_dir);
|
375 |
-
$plugin_fonts = @require $dompdf_font_dir . $font_cache_files['cache'];
|
376 |
-
|
377 |
-
foreach ($plugin_fonts as $font_family => $filenames) {
|
378 |
-
foreach ($filenames as $filename) {
|
379 |
-
foreach ($extensions as $extension) {
|
380 |
-
$file = $filename.$extension;
|
381 |
-
if (file_exists($file)) {
|
382 |
-
$dest = $path . basename($file);
|
383 |
-
copy($file, $dest);
|
384 |
-
}
|
385 |
-
}
|
386 |
-
}
|
387 |
-
}
|
388 |
-
}
|
389 |
-
}
|
390 |
-
|
391 |
-
public function disable_free_attachment( $attach, $order, $email_id, $document_type ) {
|
392 |
-
// prevent fatal error for non-order objects
|
393 |
-
if ( !method_exists( $order, 'get_total' ) ) {
|
394 |
-
return false;
|
395 |
-
}
|
396 |
-
|
397 |
-
$document_settings = WPO_WCPDF()->settings->get_document_settings( $document_type );
|
398 |
-
// echo '<pre>';var_dump($document_type);echo '</pre>';
|
399 |
-
// error_log( var_export($document_settings,true) );
|
400 |
-
|
401 |
-
// check order total & setting
|
402 |
-
$order_total = $order->get_total();
|
403 |
-
if ( $order_total == 0 && isset( $document_settings['disable_free'] ) ) {
|
404 |
-
return false;
|
405 |
-
}
|
406 |
-
|
407 |
-
return $attach;
|
408 |
-
}
|
409 |
-
|
410 |
-
/**
|
411 |
-
* Adds spans around placeholders to be able to make replacement (page count) and css (page number)
|
412 |
-
*/
|
413 |
-
public function format_page_number_placeholders ( $html, $document ) {
|
414 |
-
$html = str_replace('{{PAGE_COUNT}}', '<span class="pagecount">^C^</span>', $html);
|
415 |
-
$html = str_replace('{{PAGE_NUM}}', '<span class="pagenum"></span>', $html );
|
416 |
-
return $html;
|
417 |
-
}
|
418 |
-
|
419 |
-
/**
|
420 |
-
* Replace {{PAGE_COUNT}} placeholder with total page count
|
421 |
-
*/
|
422 |
-
public function page_number_replacements ( $dompdf, $html ) {
|
423 |
-
$placeholder = '^C^';
|
424 |
-
// create placeholder version with ASCII 0 spaces (dompdf 0.8)
|
425 |
-
$placeholder_0 = '';
|
426 |
-
$placeholder_chars = str_split($placeholder);
|
427 |
-
foreach ($placeholder_chars as $placeholder_char) {
|
428 |
-
$placeholder_0 .= chr(0).$placeholder_char;
|
429 |
-
}
|
430 |
-
|
431 |
-
// check if placeholder is used
|
432 |
-
if (strpos($html, $placeholder) !== false ) {
|
433 |
-
foreach ($dompdf->get_canvas()->get_cpdf()->objects as &$object) {
|
434 |
-
if (array_key_exists("c", $object) && strpos($object["c"], $placeholder) !== false ) {
|
435 |
-
$object["c"] = str_replace( array($placeholder,$placeholder_0) , $dompdf->get_canvas()->get_page_count() , $object["c"] );
|
436 |
-
} elseif (array_key_exists("c", $object) && strpos($object["c"], $placeholder_0) !== false ) {
|
437 |
-
$object["c"] = str_replace( array($placeholder,$placeholder_0) , chr(0).$dompdf->get_canvas()->get_page_count() , $object["c"] );
|
438 |
-
}
|
439 |
-
}
|
440 |
-
}
|
441 |
-
|
442 |
-
return $dompdf;
|
443 |
-
}
|
444 |
-
|
445 |
-
/**
|
446 |
-
* Use currency symbol font (when enabled in options)
|
447 |
-
*/
|
448 |
-
public function use_currency_font ( $document_type, $document ) {
|
449 |
-
add_filter( 'woocommerce_currency_symbol', array( $this, 'wrap_currency_symbol' ), 999, 2);
|
450 |
-
add_action( 'wpo_wcpdf_custom_styles', array($this, 'currency_symbol_font_styles' ) );
|
451 |
-
}
|
452 |
-
|
453 |
-
public function wrap_currency_symbol( $currency_symbol, $currency ) {
|
454 |
-
$currency_symbol = sprintf( '<span class="wcpdf-currency-symbol">%s</span>', $currency_symbol );
|
455 |
-
return $currency_symbol;
|
456 |
-
}
|
457 |
-
|
458 |
-
public function currency_symbol_font_styles () {
|
459 |
-
?>
|
460 |
-
.wcpdf-currency-symbol { font-family: 'Currencies'; }
|
461 |
-
<?php
|
462 |
-
}
|
463 |
-
|
464 |
-
/**
|
465 |
-
* Remove attachments older than 1 week (daily, hooked into wp_scheduled_delete )
|
466 |
-
*/
|
467 |
-
public function attachments_cleanup() {
|
468 |
-
if ( !function_exists("glob") || !function_exists('filemtime')) {
|
469 |
-
// glob is disabled
|
470 |
-
return;
|
471 |
-
}
|
472 |
-
|
473 |
-
$delete_timestamp = time() - ( DAY_IN_SECONDS * 7 );
|
474 |
-
|
475 |
-
$tmp_path = $this->get_tmp_path('attachments');
|
476 |
-
|
477 |
-
if ( $files = glob( $tmp_path.'*.pdf' ) ) { // get all pdf files
|
478 |
-
foreach( $files as $file ) {
|
479 |
-
if( is_file( $file ) ) {
|
480 |
-
$file_timestamp = filemtime( $file );
|
481 |
-
if ( !empty( $file_timestamp ) && $file_timestamp < $delete_timestamp ) {
|
482 |
-
@unlink($file);
|
483 |
-
}
|
484 |
-
}
|
485 |
-
}
|
486 |
-
}
|
487 |
-
|
488 |
-
}
|
489 |
-
|
490 |
-
/**
|
491 |
-
* Enable PHP error output
|
492 |
-
*/
|
493 |
-
public function enable_debug () {
|
494 |
-
error_reporting( E_ALL );
|
495 |
-
ini_set( 'display_errors', 1 );
|
496 |
-
}
|
497 |
-
|
498 |
-
}
|
499 |
-
|
500 |
-
endif; // class_exists
|
501 |
-
|
502 |
-
return new Main();
|
1 |
+
<?php
|
2 |
+
namespace WPO\WC\PDF_Invoices;
|
3 |
+
|
4 |
+
use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
|
5 |
+
use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
|
6 |
+
use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
|
7 |
+
|
8 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
9 |
+
exit; // Exit if accessed directly
|
10 |
+
}
|
11 |
+
|
12 |
+
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Main' ) ) :
|
13 |
+
|
14 |
+
class Main {
|
15 |
+
|
16 |
+
function __construct() {
|
17 |
+
add_action( 'wp_ajax_generate_wpo_wcpdf', array($this, 'generate_pdf_ajax' ) );
|
18 |
+
add_filter( 'woocommerce_email_attachments', array( $this, 'attach_pdf_to_email' ), 99, 3 );
|
19 |
+
add_filter( 'wpo_wcpdf_custom_attachment_condition', array( $this, 'disable_free_attachment'), 10, 4 );
|
20 |
+
|
21 |
+
if ( isset(WPO_WCPDF()->settings->debug_settings['enable_debug']) ) {
|
22 |
+
$this->enable_debug();
|
23 |
+
}
|
24 |
+
if ( isset(WPO_WCPDF()->settings->debug_settings['html_output']) ) {
|
25 |
+
add_filter( 'wpo_wcpdf_use_path', '__return_false' );
|
26 |
+
}
|
27 |
+
|
28 |
+
// include template specific custom functions
|
29 |
+
$template_path = WPO_WCPDF()->settings->get_template_path();
|
30 |
+
if ( file_exists( $template_path . '/template-functions.php' ) ) {
|
31 |
+
require_once( $template_path . '/template-functions.php' );
|
32 |
+
}
|
33 |
+
|
34 |
+
// page numbers & currency filters
|
35 |
+
add_action( 'wpo_wcpdf_get_html', array($this, 'format_page_number_placeholders' ), 10, 2 );
|
36 |
+
add_action( 'wpo_wcpdf_after_dompdf_render', array($this, 'page_number_replacements' ), 9, 2 );
|
37 |
+
if ( isset( WPO_WCPDF()->settings->general_settings['currency_font'] ) ) {
|
38 |
+
add_action( 'wpo_wcpdf_before_pdf', array($this, 'use_currency_font' ), 10, 2 );
|
39 |
+
}
|
40 |
+
|
41 |
+
// scheduled attachments cleanup - disabled for now
|
42 |
+
// add_action( 'wp_scheduled_delete', array( $this, 'attachments_cleanup') );
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Attach PDF to WooCommerce email
|
47 |
+
*/
|
48 |
+
public function attach_pdf_to_email ( $attachments, $email_id, $order ) {
|
49 |
+
// check if all variables properly set
|
50 |
+
if ( !is_object( $order ) || !isset( $email_id ) ) {
|
51 |
+
return $attachments;
|
52 |
+
}
|
53 |
+
|
54 |
+
// Skip User emails
|
55 |
+
if ( get_class( $order ) == 'WP_User' ) {
|
56 |
+
return $attachments;
|
57 |
+
}
|
58 |
+
|
59 |
+
$order_id = WCX_Order::get_id( $order );
|
60 |
+
|
61 |
+
if ( get_class( $order ) !== 'WC_Order' && $order_id == false ) {
|
62 |
+
return $attachments;
|
63 |
+
}
|
64 |
+
|
65 |
+
// WooCommerce Booking compatibility
|
66 |
+
if ( get_post_type( $order_id ) == 'wc_booking' && isset($order->order) ) {
|
67 |
+
// $order is actually a WC_Booking object!
|
68 |
+
$order = $order->order;
|
69 |
+
}
|
70 |
+
|
71 |
+
// do not process low stock notifications, user emails etc!
|
72 |
+
if ( in_array( $email_id, array( 'no_stock', 'low_stock', 'backorder', 'customer_new_account', 'customer_reset_password' ) ) || get_post_type( $order_id ) != 'shop_order' ) {
|
73 |
+
return $attachments;
|
74 |
+
}
|
75 |
+
|
76 |
+
$tmp_path = $this->get_tmp_path('attachments');
|
77 |
+
|
78 |
+
// clear pdf files from temp folder (from http://stackoverflow.com/a/13468943/1446634)
|
79 |
+
// array_map('unlink', ( glob( $tmp_path.'*.pdf' ) ? glob( $tmp_path.'*.pdf' ) : array() ) );
|
80 |
+
|
81 |
+
// disable deprecation notices during email sending
|
82 |
+
add_filter( 'wcpdf_disable_deprecation_notices', '__return_true' );
|
83 |
+
|
84 |
+
$attach_to_document_types = $this->get_documents_for_email( $email_id, $order );
|
85 |
+
foreach ( $attach_to_document_types as $document_type ) {
|
86 |
+
do_action( 'wpo_wcpdf_before_attachment_creation', $order, $email_id, $document_type );
|
87 |
+
|
88 |
+
try {
|
89 |
+
// prepare document
|
90 |
+
$document = wcpdf_get_document( $document_type, (array) $order_id, true );
|
91 |
+
if ( !$document ) { // something went wrong, continue trying with other documents
|
92 |
+
continue;
|
93 |
+
}
|
94 |
+
|
95 |
+
// get pdf data & store
|
96 |
+
$pdf_data = $document->get_pdf();
|
97 |
+
$filename = $document->get_filename();
|
98 |
+
$pdf_path = $tmp_path . $filename;
|
99 |
+
file_put_contents ( $pdf_path, $pdf_data );
|
100 |
+
$attachments[] = $pdf_path;
|
101 |
+
|
102 |
+
do_action( 'wpo_wcpdf_email_attachment', $pdf_path, $document_type );
|
103 |
+
} catch (Exception $e) {
|
104 |
+
error_log($e->getMessage());
|
105 |
+
continue;
|
106 |
+
}
|
107 |
+
}
|
108 |
+
|
109 |
+
remove_filter( 'wcpdf_disable_deprecation_notices', '__return_true' );
|
110 |
+
|
111 |
+
return $attachments;
|
112 |
+
}
|
113 |
+
|
114 |
+
public function get_documents_for_email( $email_id, $order ) {
|
115 |
+
$documents = WPO_WCPDF()->documents->get_documents();
|
116 |
+
|
117 |
+
$attach_documents = array();
|
118 |
+
foreach ($documents as $document) {
|
119 |
+
$attach_documents[ $document->get_type() ] = $document->get_attach_to_email_ids();
|
120 |
+
}
|
121 |
+
$attach_documents = apply_filters('wpo_wcpdf_attach_documents', $attach_documents );
|
122 |
+
|
123 |
+
$document_types = array();
|
124 |
+
foreach ($attach_documents as $document_type => $attach_to_email_ids ) {
|
125 |
+
// legacy settings: convert abbreviated email_ids
|
126 |
+
foreach ($attach_to_email_ids as $key => $attach_to_email_id) {
|
127 |
+
if ($attach_to_email_id == 'completed' || $attach_to_email_id == 'processing') {
|
128 |
+
$attach_to_email_ids[$key] = "customer_" . $attach_to_email_id . "_order";
|
129 |
+
}
|
130 |
+
}
|
131 |
+
|
132 |
+
$extra_condition = apply_filters('wpo_wcpdf_custom_attachment_condition', true, $order, $email_id, $document_type );
|
133 |
+
if ( in_array( $email_id, $attach_to_email_ids ) && $extra_condition === true ) {
|
134 |
+
$document_types[] = $document_type;
|
135 |
+
}
|
136 |
+
}
|
137 |
+
|
138 |
+
return $document_types;
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Load and generate the template output with ajax
|
143 |
+
*/
|
144 |
+
public function generate_pdf_ajax() {
|
145 |
+
// Check the nonce
|
146 |
+
if( empty( $_GET['action'] ) || !check_admin_referer( $_GET['action'] ) ) {
|
147 |
+
wp_die( __( 'You do not have sufficient permissions to access this page.', 'woocommerce-pdf-invoices-packing-slips' ) );
|
148 |
+
}
|
149 |
+
|
150 |
+
// Check if all parameters are set
|
151 |
+
if ( empty( $_GET['document_type'] ) && !empty( $_GET['template_type'] ) ) {
|
152 |
+
$_GET['document_type'] = $_GET['template_type'];
|
153 |
+
}
|
154 |
+
|
155 |
+
if ( empty( $_GET['order_ids'] ) ) {
|
156 |
+
wp_die( __( "You haven't selected any orders", 'woocommerce-pdf-invoices-packing-slips' ) );
|
157 |
+
}
|
158 |
+
|
159 |
+
if( empty( $_GET['document_type'] ) ) {
|
160 |
+
wp_die( __( 'Some of the export parameters are missing.', 'woocommerce-pdf-invoices-packing-slips' ) );
|
161 |
+
}
|
162 |
+
|
163 |
+
// Generate the output
|
164 |
+
$document_type = sanitize_text_field( $_GET['document_type'] );
|
165 |
+
|
166 |
+
$order_ids = (array) array_map( 'absint', explode( 'x', $_GET['order_ids'] ) );
|
167 |
+
// Process oldest first: reverse $order_ids array
|
168 |
+
$order_ids = array_reverse( $order_ids );
|
169 |
+
|
170 |
+
// set default is allowed
|
171 |
+
$allowed = true;
|
172 |
+
|
173 |
+
// check if user is logged in
|
174 |
+
if ( ! is_user_logged_in() ) {
|
175 |
+
$allowed = false;
|
176 |
+
}
|
177 |
+
|
178 |
+
// Check the user privileges
|
179 |
+
if( !( current_user_can( 'manage_woocommerce_orders' ) || current_user_can( 'edit_shop_orders' ) ) && !isset( $_GET['my-account'] ) ) {
|
180 |
+
$allowed = false;
|
181 |
+
}
|
182 |
+
|
183 |
+
// User call from my-account page
|
184 |
+
if ( !current_user_can('manage_options') && isset( $_GET['my-account'] ) ) {
|
185 |
+
// Only for single orders!
|
186 |
+
if ( count( $order_ids ) > 1 ) {
|
187 |
+
$allowed = false;
|
188 |
+
}
|
189 |
+
|
190 |
+
// Check if current user is owner of order IMPORTANT!!!
|
191 |
+
if ( ! current_user_can( 'view_order', $order_ids[0] ) ) {
|
192 |
+
$allowed = false;
|
193 |
+
}
|
194 |
+
}
|
195 |
+
|
196 |
+
$allowed = apply_filters( 'wpo_wcpdf_check_privs', $allowed, $order_ids );
|
197 |
+
|
198 |
+
if ( ! $allowed ) {
|
199 |
+
wp_die( __( 'You do not have sufficient permissions to access this page.', 'woocommerce-pdf-invoices-packing-slips' ) );
|
200 |
+
}
|
201 |
+
|
202 |
+
// if we got here, we're safe to go!
|
203 |
+
try {
|
204 |
+
$document = wcpdf_get_document( $document_type, $order_ids, true );
|
205 |
+
|
206 |
+
if ( $document ) {
|
207 |
+
$output_format = WPO_WCPDF()->settings->get_output_format( $document_type );
|
208 |
+
switch ( $output_format ) {
|
209 |
+
case 'html':
|
210 |
+
$document->output_html();
|
211 |
+
break;
|
212 |
+
case 'pdf':
|
213 |
+
default:
|
214 |
+
if ( has_action( 'wpo_wcpdf_created_manually' ) ) {
|
215 |
+
do_action( 'wpo_wcpdf_created_manually', $document->get_pdf(), $document->get_filename() );
|
216 |
+
}
|
217 |
+
$output_mode = WPO_WCPDF()->settings->get_output_mode( $document_type );
|
218 |
+
$document->output_pdf( $output_mode );
|
219 |
+
break;
|
220 |
+
}
|
221 |
+
} else {
|
222 |
+
wp_die( sprintf( __( "Document of type '%s' for the selected order(s) could not be generated", 'woocommerce-pdf-invoices-packing-slips' ), $document_type ) );
|
223 |
+
}
|
224 |
+
} catch (Exception $e) {
|
225 |
+
echo $e->getMessage();
|
226 |
+
}
|
227 |
+
|
228 |
+
exit;
|
229 |
+
}
|
230 |
+
|
231 |
+
/**
|
232 |
+
* Return tmp path for different plugin processes
|
233 |
+
*/
|
234 |
+
public function get_tmp_path ( $type = '' ) {
|
235 |
+
$tmp_base = $this->get_tmp_base();
|
236 |
+
// check if tmp folder exists => if not, initialize
|
237 |
+
if ( !@is_dir( $tmp_base ) ) {
|
238 |
+
$this->init_tmp( $tmp_base );
|
239 |
+
}
|
240 |
+
|
241 |
+
if ( empty( $type ) ) {
|
242 |
+
return $tmp_base;
|
243 |
+
}
|
244 |
+
|
245 |
+
switch ( $type ) {
|
246 |
+
case 'dompdf':
|
247 |
+
$tmp_path = $tmp_base . 'dompdf';
|
248 |
+
break;
|
249 |
+
case 'font_cache':
|
250 |
+
case 'fonts':
|
251 |
+
$tmp_path = $tmp_base . 'fonts';
|
252 |
+
break;
|
253 |
+
case 'attachments':
|
254 |
+
$tmp_path = $tmp_base . 'attachments/';
|
255 |
+
break;
|
256 |
+
default:
|
257 |
+
$tmp_path = $tmp_base . $type;
|
258 |
+
break;
|
259 |
+
}
|
260 |
+
|
261 |
+
// double check for existence, in case tmp_base was installed, but subfolder not created
|
262 |
+
if ( !@is_dir( $tmp_path ) ) {
|
263 |
+
@mkdir( $tmp_path );
|
264 |
+
}
|
265 |
+
|
266 |
+
return $tmp_path;
|
267 |
+
}
|
268 |
+
|
269 |
+
/**
|
270 |
+
* return the base tmp folder (usually uploads)
|
271 |
+
*/
|
272 |
+
public function get_tmp_base () {
|
273 |
+
// wp_upload_dir() is used to set the base temp folder, under which a
|
274 |
+
// 'wpo_wcpdf' folder and several subfolders are created
|
275 |
+
//
|
276 |
+
// wp_upload_dir() will:
|
277 |
+
// * default to WP_CONTENT_DIR/uploads
|
278 |
+
// * UNLESS the ‘UPLOADS’ constant is defined in wp-config (http://codex.wordpress.org/Editing_wp-config.php#Moving_uploads_folder)
|
279 |
+
//
|
280 |
+
// May also be overridden by the wpo_wcpdf_tmp_path filter
|
281 |
+
|
282 |
+
$upload_dir = wp_upload_dir();
|
283 |
+
$upload_base = trailingslashit( $upload_dir['basedir'] );
|
284 |
+
$tmp_base = trailingslashit( apply_filters( 'wpo_wcpdf_tmp_path', $upload_base . 'wpo_wcpdf/' ) );
|
285 |
+
return $tmp_base;
|
286 |
+
}
|
287 |
+
|
288 |
+
/**
|
289 |
+
* Install/create plugin tmp folders
|
290 |
+
*/
|
291 |
+
public function init_tmp ( $tmp_base ) {
|
292 |
+
// create plugin base temp folder
|
293 |
+
@mkdir( $tmp_base );
|
294 |
+
|
295 |
+
// create subfolders & protect
|
296 |
+
$subfolders = array( 'attachments', 'fonts', 'dompdf' );
|
297 |
+
foreach ( $subfolders as $subfolder ) {
|
298 |
+
$path = $tmp_base . $subfolder . '/';
|
299 |
+
@mkdir( $path );
|
300 |
+
|
301 |
+
// copy font files
|
302 |
+
if ( $subfolder == 'fonts' ) {
|
303 |
+
$this->copy_fonts( $path, false );
|
304 |
+
}
|
305 |
+
|
306 |
+
// create .htaccess file and empty index.php to protect in case an open webfolder is used!
|
307 |
+
@file_put_contents( $path . '.htaccess', 'deny from all' );
|
308 |
+
@touch( $path . 'index.php' );
|
309 |
+
}
|
310 |
+
|
311 |
+
}
|
312 |
+
|
313 |
+
/**
|
314 |
+
* Copy DOMPDF fonts to wordpress tmp folder
|
315 |
+
*/
|
316 |
+
public function copy_fonts ( $path, $merge_with_local = true ) {
|
317 |
+
$path = trailingslashit( $path );
|
318 |
+
$dompdf_font_dir = WPO_WCPDF()->plugin_path() . "/vendor/dompdf/dompdf/lib/fonts/";
|
319 |
+
|
320 |
+
// get local font dir from filtered options
|
321 |
+
$dompdf_options = apply_filters( 'wpo_wcpdf_dompdf_options', array(
|
322 |
+
'defaultFont' => 'dejavu sans',
|
323 |
+
'tempDir' => $this->get_tmp_path('dompdf'),
|
324 |
+
'logOutputFile' => $this->get_tmp_path('dompdf') . "/log.htm",
|
325 |
+
'fontDir' => $this->get_tmp_path('fonts'),
|
326 |
+
'fontCache' => $this->get_tmp_path('fonts'),
|
327 |
+
'isRemoteEnabled' => true,
|
328 |
+
'isFontSubsettingEnabled' => true,
|
329 |
+
'isHtml5ParserEnabled' => true,
|
330 |
+
) );
|
331 |
+
$fontDir = $dompdf_options['fontDir'];
|
332 |
+
|
333 |
+
// merge font family cache with local/custom if present
|
334 |
+
$font_cache_files = array(
|
335 |
+
'cache' => 'dompdf_font_family_cache.php',
|
336 |
+
'cache_dist' => 'dompdf_font_family_cache.dist.php',
|
337 |
+
);
|
338 |
+
foreach ( $font_cache_files as $font_cache_name => $font_cache_filename ) {
|
339 |
+
$plugin_fonts = @require $dompdf_font_dir . $font_cache_filename;
|
340 |
+
if ( $merge_with_local && is_readable( $path . $font_cache_filename ) ) {
|
341 |
+
$local_fonts = @require $path . $font_cache_filename;
|
342 |
+
if (is_array($local_fonts) && is_array($plugin_fonts)) {
|
343 |
+
// merge local & plugin fonts, plugin fonts overwrite (update) local fonts
|
344 |
+
// while custom local fonts are retained
|
345 |
+
$local_fonts = array_merge($local_fonts, $plugin_fonts);
|
346 |
+
// create readable array with $fontDir in place of the actual folder for portability
|
347 |
+
$fonts_export = var_export($local_fonts,true);
|
348 |
+
$fonts_export = str_replace('\'' . $fontDir , '$fontDir . \'', $fonts_export);
|
349 |
+
$cacheData = sprintf("<?php return %s;%s?>", $fonts_export, PHP_EOL );
|
350 |
+
// write file with merged cache data
|
351 |
+
file_put_contents($path . $font_cache_filename, $cacheData);
|
352 |
+
} else { // empty local file
|
353 |
+
copy( $dompdf_font_dir . $font_cache_filename, $path . $font_cache_filename );
|
354 |
+
}
|
355 |
+
} else {
|
356 |
+
// we couldn't read the local font cache file so we're simply copying over plugin cache file
|
357 |
+
copy( $dompdf_font_dir . $font_cache_filename, $path . $font_cache_filename );
|
358 |
+
}
|
359 |
+
}
|
360 |
+
|
361 |
+
// first try the easy way with glob!
|
362 |
+
if ( function_exists('glob') ) {
|
363 |
+
$files = glob($dompdf_font_dir."*.*");
|
364 |
+
foreach($files as $file){
|
365 |
+
$filename = basename($file);
|
366 |
+
if( !is_dir($file) && is_readable($file) && !in_array($filename, $font_cache_files)) {
|
367 |
+
$dest = $path . $filename;
|
368 |
+
copy($file, $dest);
|
369 |
+
}
|
370 |
+
}
|
371 |
+
} else {
|
372 |
+
// fallback method using font cache file (glob is disabled on some servers with disable_functions)
|
373 |
+
$extensions = array( '.ttf', '.ufm', '.ufm.php', '.afm', '.afm.php' );
|
374 |
+
$fontDir = untrailingslashit($dompdf_font_dir);
|
375 |
+
$plugin_fonts = @require $dompdf_font_dir . $font_cache_files['cache'];
|
376 |
+
|
377 |
+
foreach ($plugin_fonts as $font_family => $filenames) {
|
378 |
+
foreach ($filenames as $filename) {
|
379 |
+
foreach ($extensions as $extension) {
|
380 |
+
$file = $filename.$extension;
|
381 |
+
if (file_exists($file)) {
|
382 |
+
$dest = $path . basename($file);
|
383 |
+
copy($file, $dest);
|
384 |
+
}
|
385 |
+
}
|
386 |
+
}
|
387 |
+
}
|
388 |
+
}
|
389 |
+
}
|
390 |
+
|
391 |
+
public function disable_free_attachment( $attach, $order, $email_id, $document_type ) {
|
392 |
+
// prevent fatal error for non-order objects
|
393 |
+
if ( !method_exists( $order, 'get_total' ) ) {
|
394 |
+
return false;
|
395 |
+
}
|
396 |
+
|
397 |
+
$document_settings = WPO_WCPDF()->settings->get_document_settings( $document_type );
|
398 |
+
// echo '<pre>';var_dump($document_type);echo '</pre>';
|
399 |
+
// error_log( var_export($document_settings,true) );
|
400 |
+
|
401 |
+
// check order total & setting
|
402 |
+
$order_total = $order->get_total();
|
403 |
+
if ( $order_total == 0 && isset( $document_settings['disable_free'] ) ) {
|
404 |
+
return false;
|
405 |
+
}
|
406 |
+
|
407 |
+
return $attach;
|
408 |
+
}
|
409 |
+
|
410 |
+
/**
|
411 |
+
* Adds spans around placeholders to be able to make replacement (page count) and css (page number)
|
412 |
+
*/
|
413 |
+
public function format_page_number_placeholders ( $html, $document ) {
|
414 |
+
$html = str_replace('{{PAGE_COUNT}}', '<span class="pagecount">^C^</span>', $html);
|
415 |
+
$html = str_replace('{{PAGE_NUM}}', '<span class="pagenum"></span>', $html );
|
416 |
+
return $html;
|
417 |
+
}
|
418 |
+
|
419 |
+
/**
|
420 |
+
* Replace {{PAGE_COUNT}} placeholder with total page count
|
421 |
+
*/
|
422 |
+
public function page_number_replacements ( $dompdf, $html ) {
|
423 |
+
$placeholder = '^C^';
|
424 |
+
// create placeholder version with ASCII 0 spaces (dompdf 0.8)
|
425 |
+
$placeholder_0 = '';
|
426 |
+
$placeholder_chars = str_split($placeholder);
|
427 |
+
foreach ($placeholder_chars as $placeholder_char) {
|
428 |
+
$placeholder_0 .= chr(0).$placeholder_char;
|
429 |
+
}
|
430 |
+
|
431 |
+
// check if placeholder is used
|
432 |
+
if (strpos($html, $placeholder) !== false ) {
|
433 |
+
foreach ($dompdf->get_canvas()->get_cpdf()->objects as &$object) {
|
434 |
+
if (array_key_exists("c", $object) && strpos($object["c"], $placeholder) !== false ) {
|
435 |
+
$object["c"] = str_replace( array($placeholder,$placeholder_0) , $dompdf->get_canvas()->get_page_count() , $object["c"] );
|
436 |
+
} elseif (array_key_exists("c", $object) && strpos($object["c"], $placeholder_0) !== false ) {
|
437 |
+
$object["c"] = str_replace( array($placeholder,$placeholder_0) , chr(0).$dompdf->get_canvas()->get_page_count() , $object["c"] );
|
438 |
+
}
|
439 |
+
}
|
440 |
+
}
|
441 |
+
|
442 |
+
return $dompdf;
|
443 |
+
}
|
444 |
+
|
445 |
+
/**
|
446 |
+
* Use currency symbol font (when enabled in options)
|
447 |
+
*/
|
448 |
+
public function use_currency_font ( $document_type, $document ) {
|
449 |
+
add_filter( 'woocommerce_currency_symbol', array( $this, 'wrap_currency_symbol' ), 999, 2);
|
450 |
+
add_action( 'wpo_wcpdf_custom_styles', array($this, 'currency_symbol_font_styles' ) );
|
451 |
+
}
|
452 |
+
|
453 |
+
public function wrap_currency_symbol( $currency_symbol, $currency ) {
|
454 |
+
$currency_symbol = sprintf( '<span class="wcpdf-currency-symbol">%s</span>', $currency_symbol );
|
455 |
+
return $currency_symbol;
|
456 |
+
}
|
457 |
+
|
458 |
+
public function currency_symbol_font_styles () {
|
459 |
+
?>
|
460 |
+
.wcpdf-currency-symbol { font-family: 'Currencies'; }
|
461 |
+
<?php
|
462 |
+
}
|
463 |
+
|
464 |
+
/**
|
465 |
+
* Remove attachments older than 1 week (daily, hooked into wp_scheduled_delete )
|
466 |
+
*/
|
467 |
+
public function attachments_cleanup() {
|
468 |
+
if ( !function_exists("glob") || !function_exists('filemtime')) {
|
469 |
+
// glob is disabled
|
470 |
+
return;
|
471 |
+
}
|
472 |
+
|
473 |
+
$delete_timestamp = time() - ( DAY_IN_SECONDS * 7 );
|
474 |
+
|
475 |
+
$tmp_path = $this->get_tmp_path('attachments');
|
476 |
+
|
477 |
+
if ( $files = glob( $tmp_path.'*.pdf' ) ) { // get all pdf files
|
478 |
+
foreach( $files as $file ) {
|
479 |
+
if( is_file( $file ) ) {
|
480 |
+
$file_timestamp = filemtime( $file );
|
481 |
+
if ( !empty( $file_timestamp ) && $file_timestamp < $delete_timestamp ) {
|
482 |
+
@unlink($file);
|
483 |
+
}
|
484 |
+
}
|
485 |
+
}
|
486 |
+
}
|
487 |
+
|
488 |
+
}
|
489 |
+
|
490 |
+
/**
|
491 |
+
* Enable PHP error output
|
492 |
+
*/
|
493 |
+
public function enable_debug () {
|
494 |
+
error_reporting( E_ALL );
|
495 |
+
ini_set( 'display_errors', 1 );
|
496 |
+
}
|
497 |
+
|
498 |
+
}
|
499 |
+
|
500 |
+
endif; // class_exists
|
501 |
+
|
502 |
+
return new Main();
|
includes/class-wcpdf-pdf-maker.php
CHANGED
@@ -1,60 +1,60 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPO\WC\PDF_Invoices;
|
3 |
-
|
4 |
-
use Dompdf\Dompdf;
|
5 |
-
use Dompdf\Options;
|
6 |
-
|
7 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
8 |
-
exit; // Exit if accessed directly
|
9 |
-
}
|
10 |
-
|
11 |
-
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\PDF_Maker' ) ) :
|
12 |
-
|
13 |
-
class PDF_Maker {
|
14 |
-
public $html;
|
15 |
-
public $settings;
|
16 |
-
|
17 |
-
public function __construct( $html, $settings = array() ) {
|
18 |
-
$this->html = $html;
|
19 |
-
|
20 |
-
$default_settings = array(
|
21 |
-
'paper_size' => 'A4',
|
22 |
-
'paper_orientation' => 'portrait',
|
23 |
-
'font_subsetting' => false,
|
24 |
-
);
|
25 |
-
$this->settings = $settings + $default_settings;
|
26 |
-
}
|
27 |
-
|
28 |
-
public function output() {
|
29 |
-
if ( empty( $this->html ) ) {
|
30 |
-
return;
|
31 |
-
}
|
32 |
-
|
33 |
-
require WPO_WCPDF()->plugin_path() . '/vendor/autoload.php';
|
34 |
-
|
35 |
-
// set options
|
36 |
-
$options = new Options( apply_filters( 'wpo_wcpdf_dompdf_options', array(
|
37 |
-
'defaultFont' => 'dejavu sans',
|
38 |
-
'tempDir' => WPO_WCPDF()->main->get_tmp_path('dompdf'),
|
39 |
-
'logOutputFile' => WPO_WCPDF()->main->get_tmp_path('dompdf') . "/log.htm",
|
40 |
-
'fontDir' => WPO_WCPDF()->main->get_tmp_path('fonts'),
|
41 |
-
'fontCache' => WPO_WCPDF()->main->get_tmp_path('fonts'),
|
42 |
-
'isRemoteEnabled' => true,
|
43 |
-
'isFontSubsettingEnabled' => $this->settings['font_subsetting'],
|
44 |
-
// HTML5 parser requires iconv
|
45 |
-
'isHtml5ParserEnabled' => ( isset(WPO_WCPDF()->settings->debug_settings['use_html5_parser']) && extension_loaded('iconv') ) ? true : false,
|
46 |
-
) ) );
|
47 |
-
|
48 |
-
// instantiate and use the dompdf class
|
49 |
-
$dompdf = new Dompdf( $options );
|
50 |
-
$dompdf->loadHtml( $this->html );
|
51 |
-
$dompdf->setPaper( $this->settings['paper_size'], $this->settings['paper_orientation'] );
|
52 |
-
$dompdf = apply_filters( 'wpo_wcpdf_before_dompdf_render', $dompdf, $this->html );
|
53 |
-
$dompdf->render();
|
54 |
-
$dompdf = apply_filters( 'wpo_wcpdf_after_dompdf_render', $dompdf, $this->html );
|
55 |
-
|
56 |
-
return $dompdf->output();
|
57 |
-
}
|
58 |
-
}
|
59 |
-
|
60 |
-
endif; // class_exists
|
1 |
+
<?php
|
2 |
+
namespace WPO\WC\PDF_Invoices;
|
3 |
+
|
4 |
+
use Dompdf\Dompdf;
|
5 |
+
use Dompdf\Options;
|
6 |
+
|
7 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
8 |
+
exit; // Exit if accessed directly
|
9 |
+
}
|
10 |
+
|
11 |
+
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\PDF_Maker' ) ) :
|
12 |
+
|
13 |
+
class PDF_Maker {
|
14 |
+
public $html;
|
15 |
+
public $settings;
|
16 |
+
|
17 |
+
public function __construct( $html, $settings = array() ) {
|
18 |
+
$this->html = $html;
|
19 |
+
|
20 |
+
$default_settings = array(
|
21 |
+
'paper_size' => 'A4',
|
22 |
+
'paper_orientation' => 'portrait',
|
23 |
+
'font_subsetting' => false,
|
24 |
+
);
|
25 |
+
$this->settings = $settings + $default_settings;
|
26 |
+
}
|
27 |
+
|
28 |
+
public function output() {
|
29 |
+
if ( empty( $this->html ) ) {
|
30 |
+
return;
|
31 |
+
}
|
32 |
+
|
33 |
+
require WPO_WCPDF()->plugin_path() . '/vendor/autoload.php';
|
34 |
+
|
35 |
+
// set options
|
36 |
+
$options = new Options( apply_filters( 'wpo_wcpdf_dompdf_options', array(
|
37 |
+
'defaultFont' => 'dejavu sans',
|
38 |
+
'tempDir' => WPO_WCPDF()->main->get_tmp_path('dompdf'),
|
39 |
+
'logOutputFile' => WPO_WCPDF()->main->get_tmp_path('dompdf') . "/log.htm",
|
40 |
+
'fontDir' => WPO_WCPDF()->main->get_tmp_path('fonts'),
|
41 |
+
'fontCache' => WPO_WCPDF()->main->get_tmp_path('fonts'),
|
42 |
+
'isRemoteEnabled' => true,
|
43 |
+
'isFontSubsettingEnabled' => $this->settings['font_subsetting'],
|
44 |
+
// HTML5 parser requires iconv
|
45 |
+
'isHtml5ParserEnabled' => ( isset(WPO_WCPDF()->settings->debug_settings['use_html5_parser']) && extension_loaded('iconv') ) ? true : false,
|
46 |
+
) ) );
|
47 |
+
|
48 |
+
// instantiate and use the dompdf class
|
49 |
+
$dompdf = new Dompdf( $options );
|
50 |
+
$dompdf->loadHtml( $this->html );
|
51 |
+
$dompdf->setPaper( $this->settings['paper_size'], $this->settings['paper_orientation'] );
|
52 |
+
$dompdf = apply_filters( 'wpo_wcpdf_before_dompdf_render', $dompdf, $this->html );
|
53 |
+
$dompdf->render();
|
54 |
+
$dompdf = apply_filters( 'wpo_wcpdf_after_dompdf_render', $dompdf, $this->html );
|
55 |
+
|
56 |
+
return $dompdf->output();
|
57 |
+
}
|
58 |
+
}
|
59 |
+
|
60 |
+
endif; // class_exists
|
includes/class-wcpdf-settings-callbacks.php
CHANGED
@@ -1,503 +1,503 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPO\WC\PDF_Invoices;
|
3 |
-
|
4 |
-
use WPO\WC\PDF_Invoices\Documents\Sequential_Number_Store;
|
5 |
-
|
6 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
7 |
-
exit; // Exit if accessed directly
|
8 |
-
}
|
9 |
-
|
10 |
-
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Settings_Callbacks' ) ) :
|
11 |
-
|
12 |
-
class Settings_Callbacks {
|
13 |
-
/**
|
14 |
-
* Section null callback.
|
15 |
-
*
|
16 |
-
* @return void.
|
17 |
-
*/
|
18 |
-
public function section() {
|
19 |
-
}
|
20 |
-
|
21 |
-
/**
|
22 |
-
* Debug section callback.
|
23 |
-
*
|
24 |
-
* @return void.
|
25 |
-
*/
|
26 |
-
public function debug_section() {
|
27 |
-
_e( '<b>Warning!</b> The settings below are meant for debugging/development only. Do not use them on a live website!' , 'woocommerce-pdf-invoices-packing-slips' );
|
28 |
-
}
|
29 |
-
|
30 |
-
/**
|
31 |
-
* Custom fields section callback.
|
32 |
-
*
|
33 |
-
* @return void.
|
34 |
-
*/
|
35 |
-
public function custom_fields_section() {
|
36 |
-
_e( 'These are used for the (optional) footer columns in the <em>Modern (Premium)</em> template, but can also be used for other elements in your custom template' , 'woocommerce-pdf-invoices-packing-slips' );
|
37 |
-
}
|
38 |
-
|
39 |
-
/**
|
40 |
-
* Checkbox callback.
|
41 |
-
*
|
42 |
-
* args:
|
43 |
-
* option_name - name of the main option
|
44 |
-
* id - key of the setting
|
45 |
-
* value - value if not 1 (optional)
|
46 |
-
* default - default setting (optional)
|
47 |
-
* description - description (optional)
|
48 |
-
*
|
49 |
-
* @return void.
|
50 |
-
*/
|
51 |
-
public function checkbox( $args ) {
|
52 |
-
extract( $this->normalize_settings_args( $args ) );
|
53 |
-
|
54 |
-
// output checkbox
|
55 |
-
printf( '<input type="checkbox" id="%1$s" name="%2$s" value="%3$s"%4$s />', $id, $setting_name, $value, checked( $value, $current, false ) );
|
56 |
-
|
57 |
-
// output description.
|
58 |
-
if ( isset( $description ) ) {
|
59 |
-
printf( '<p class="description">%s</p>', $description );
|
60 |
-
}
|
61 |
-
}
|
62 |
-
|
63 |
-
/**
|
64 |
-
* Text input callback.
|
65 |
-
*
|
66 |
-
* args:
|
67 |
-
* option_name - name of the main option
|
68 |
-
* id - key of the setting
|
69 |
-
* size - size of the text input (em)
|
70 |
-
* default - default setting (optional)
|
71 |
-
* description - description (optional)
|
72 |
-
* type - type (optional)
|
73 |
-
*
|
74 |
-
* @return void.
|
75 |
-
*/
|
76 |
-
public function text_input( $args ) {
|
77 |
-
extract( $this->normalize_settings_args( $args ) );
|
78 |
-
|
79 |
-
if (empty($type)) {
|
80 |
-
$type = 'text';
|
81 |
-
}
|
82 |
-
|
83 |
-
printf( '<input type="%1$s" id="%2$s" name="%3$s" value="%4$s" size="%5$s" placeholder="%6$s"/>', $type, $id, $setting_name, esc_attr( $current ), $size, $placeholder );
|
84 |
-
|
85 |
-
// output description.
|
86 |
-
if ( isset( $description ) ) {
|
87 |
-
printf( '<p class="description">%s</p>', $description );
|
88 |
-
}
|
89 |
-
}
|
90 |
-
|
91 |
-
// Single text option (not part of any settings array)
|
92 |
-
public function singular_text_element( $args ) {
|
93 |
-
$option_name = $args['option_name'];
|
94 |
-
$id = $args['id'];
|
95 |
-
$size = isset( $args['size'] ) ? $args['size'] : '25';
|
96 |
-
$class = isset( $args['translatable'] ) && $args['translatable'] === true ? 'translatable' : '';
|
97 |
-
|
98 |
-
$option = get_option( $option_name );
|
99 |
-
|
100 |
-
if ( isset( $option ) ) {
|
101 |
-
$current = $option;
|
102 |
-
} else {
|
103 |
-
$current = isset( $args['default'] ) ? $args['default'] : '';
|
104 |
-
}
|
105 |
-
|
106 |
-
$html = sprintf( '<input type="text" id="%1$s" name="%2$s" value="%3$s" size="%4$s" class="%5$s"/>', $id, $option_name, $current, $size, $class );
|
107 |
-
|
108 |
-
// Displays option description.
|
109 |
-
if ( isset( $args['description'] ) ) {
|
110 |
-
$html .= sprintf( '<p class="description">%s</p>', $args['description'] );
|
111 |
-
}
|
112 |
-
|
113 |
-
echo $html;
|
114 |
-
}
|
115 |
-
|
116 |
-
|
117 |
-
/**
|
118 |
-
* Textarea callback.
|
119 |
-
*
|
120 |
-
* args:
|
121 |
-
* option_name - name of the main option
|
122 |
-
* id - key of the setting
|
123 |
-
* width - width of the text input (em)
|
124 |
-
* height - height of the text input (lines)
|
125 |
-
* default - default setting (optional)
|
126 |
-
* description - description (optional)
|
127 |
-
*
|
128 |
-
* @return void.
|
129 |
-
*/
|
130 |
-
public function textarea( $args ) {
|
131 |
-
extract( $this->normalize_settings_args( $args ) );
|
132 |
-
|
133 |
-
printf( '<textarea id="%1$s" name="%2$s" cols="%4$s" rows="%5$s" placeholder="%6$s"/>%3$s</textarea>', $id, $setting_name, esc_textarea( $current ), $width, $height, $placeholder );
|
134 |
-
|
135 |
-
// output description.
|
136 |
-
if ( isset( $description ) ) {
|
137 |
-
printf( '<p class="description">%s</p>', $description );
|
138 |
-
}
|
139 |
-
}
|
140 |
-
|
141 |
-
/**
|
142 |
-
* Select element callback.
|
143 |
-
*
|
144 |
-
* @param array $args Field arguments.
|
145 |
-
*
|
146 |
-
* @return string Select field.
|
147 |
-
*/
|
148 |
-
public function select( $args ) {
|
149 |
-
extract( $this->normalize_settings_args( $args ) );
|
150 |
-
|
151 |
-
printf( '<select id="%1$s" name="%2$s">', $id, $setting_name );
|
152 |
-
|
153 |
-
foreach ( $options as $key => $label ) {
|
154 |
-
printf( '<option value="%s"%s>%s</option>', $key, selected( $current, $key, false ), $label );
|
155 |
-
}
|
156 |
-
|
157 |
-
echo '</select>';
|
158 |
-
|
159 |
-
if (isset($custom)) {
|
160 |
-
printf( '<div class="%1$s_custom custom">', $id );
|
161 |
-
|
162 |
-
if (is_callable( array( $this, $custom['type'] ) ) ) {
|
163 |
-
$this->{$custom['type']}( $custom['args'] );
|
164 |
-
}
|
165 |
-
echo '</div>';
|
166 |
-
?>
|
167 |
-
<script type="text/javascript">
|
168 |
-
jQuery(document).ready(function($) {
|
169 |
-
function check_<?php echo $id; ?>_custom() {
|
170 |
-
var custom = $('#<?php echo $id; ?>').val();
|
171 |
-
if (custom == 'custom') {
|
172 |
-
$( '.<?php echo $id; ?>_custom').show();
|
173 |
-
} else {
|
174 |
-
$( '.<?php echo $id; ?>_custom').hide();
|
175 |
-
}
|
176 |
-
}
|
177 |
-
|
178 |
-
check_<?php echo $id; ?>_custom();
|
179 |
-
|
180 |
-
$( '#<?php echo $id; ?>' ).change(function() {
|
181 |
-
check_<?php echo $id; ?>_custom();
|
182 |
-
});
|
183 |
-
|
184 |
-
});
|
185 |
-
</script>
|
186 |
-
<?php
|
187 |
-
}
|
188 |
-
|
189 |
-
// Displays option description.
|
190 |
-
if ( isset( $args['description'] ) ) {
|
191 |
-
printf( '<p class="description">%s</p>', $args['description'] );
|
192 |
-
}
|
193 |
-
|
194 |
-
}
|
195 |
-
|
196 |
-
public function radio_button( $args ) {
|
197 |
-
extract( $this->normalize_settings_args( $args ) );
|
198 |
-
|
199 |
-
foreach ( $options as $key => $label ) {
|
200 |
-
printf( '<input type="radio" class="radio" id="%1$s[%3$s]" name="%2$s" value="%3$s"%4$s />', $id, $setting_name, $key, checked( $current, $key, false ) );
|
201 |
-
printf( '<label for="%1$s[%3$s]"> %4$s</label><br>', $id, $setting_name, $key, $label);
|
202 |
-
}
|
203 |
-
|
204 |
-
|
205 |
-
// Displays option description.
|
206 |
-
if ( isset( $args['description'] ) ) {
|
207 |
-
printf( '<p class="description">%s</p>', $args['description'] );
|
208 |
-
}
|
209 |
-
|
210 |
-
}
|
211 |
-
|
212 |
-
/**
|
213 |
-
* Multiple text element callback.
|
214 |
-
* @param array $args Field arguments.
|
215 |
-
* @return string Text input field.
|
216 |
-
*/
|
217 |
-
public function multiple_text_input( $args ) {
|
218 |
-
extract( $this->normalize_settings_args( $args ) );
|
219 |
-
|
220 |
-
if (!empty($header)) {
|
221 |
-
echo "<p><strong>{$header}</strong>:</p>";
|
222 |
-
}
|
223 |
-
|
224 |
-
printf('<p class="%s multiple-text-input">', $id);
|
225 |
-
foreach ($fields as $name => $field) {
|
226 |
-
$size = $field['size'];
|
227 |
-
$placeholder = isset( $field['placeholder'] ) ? $field['placeholder'] : '';
|
228 |
-
|
229 |
-
if (isset($field['label_width'])) {
|
230 |
-
$style = sprintf( 'style="display:inline-block; width:%1$s;"', $field['label_width'] );
|
231 |
-
} else {
|
232 |
-
$style = '';
|
233 |
-
}
|
234 |
-
|
235 |
-
$description = isset( $field['description'] ) ? '<span style="font-style:italic;">'.$field['description'].'</span>' : '';
|
236 |
-
|
237 |
-
// output field label
|
238 |
-
if (isset($field['label'])) {
|
239 |
-
printf( '<label for="%1$s_%2$s" %3$s>%4$s:</label>', $id, $name, $style, $field['label'] );
|
240 |
-
}
|
241 |
-
|
242 |
-
// output field
|
243 |
-
$field_current = isset($current[$name]) ? $current[$name] : '';
|
244 |
-
$type = isset( $field['type'] ) ? $field['type'] : 'text';
|
245 |
-
printf( '<input type="%1$s" id="%2$s_%4$s" name="%3$s[%4$s]" value="%5$s" size="%6$s" placeholder="%7$s"/> %8$s<br/>', $type, $id, $setting_name, $name, esc_attr( $field_current ), $size, $placeholder, $description );
|
246 |
-
}
|
247 |
-
echo "</p>";
|
248 |
-
|
249 |
-
// Displays option description.
|
250 |
-
if ( isset( $args['description'] ) ) {
|
251 |
-
printf( '<p class="description">%s</p>', $args['description'] );
|
252 |
-
}
|
253 |
-
}
|
254 |
-
|
255 |
-
/**
|
256 |
-
* Multiple text element callback.
|
257 |
-
* @param array $args Field arguments.
|
258 |
-
* @return string Text input field.
|
259 |
-
*/
|
260 |
-
public function multiple_checkboxes( $args ) {
|
261 |
-
extract( $this->normalize_settings_args( $args ) );
|
262 |
-
|
263 |
-
foreach ($fields as $name => $label) {
|
264 |
-
// $label = $field['label'];
|
265 |
-
|
266 |
-
// output checkbox
|
267 |
-
$field_current = isset($current[$name]) ? $current[$name] : '';
|
268 |
-
printf( '<input type="checkbox" id="%1$s_%3$s" name="%2$s[%3$s]" value="%4$s"%5$s />', $id, $setting_name, $name, $value, checked( $value, $field_current, false ) );
|
269 |
-
|
270 |
-
// output field label
|
271 |
-
printf( '<label for="%1$s_%2$s">%3$s</label><br>', $id, $name, $label );
|
272 |
-
|
273 |
-
}
|
274 |
-
|
275 |
-
// Displays option description.
|
276 |
-
if ( isset( $args['description'] ) ) {
|
277 |
-
printf( '<p class="description">%s</p>', $args['description'] );
|
278 |
-
}
|
279 |
-
}
|
280 |
-
|
281 |
-
/**
|
282 |
-
* Media upload callback.
|
283 |
-
*
|
284 |
-
* @param array $args Field arguments.
|
285 |
-
*
|
286 |
-
* @return string Media upload button & preview.
|
287 |
-
*/
|
288 |
-
public function media_upload( $args ) {
|
289 |
-
extract( $this->normalize_settings_args( $args ) );
|
290 |
-
|
291 |
-
if( !empty($current) ) {
|
292 |
-
$attachment = wp_get_attachment_image_src( $current, 'full', false );
|
293 |
-
|
294 |
-
$attachment_src = $attachment[0];
|
295 |
-
$attachment_width = $attachment[1];
|
296 |
-
$attachment_height = $attachment[2];
|
297 |
-
$attachment_resolution = round($attachment_height/(3/2.54));
|
298 |
-
|
299 |
-
printf('<img src="%1$s" style="display:block" id="img-%4$s"/>', $attachment_src, $attachment_width, $attachment_height, $id );
|
300 |
-
printf('<div class="attachment-resolution"><p class="description">%s: %sdpi (default height = 3cm)</p></div>', __('Image resolution'), $attachment_resolution );
|
301 |
-
printf('<span class="button wpo_remove_image_button" data-input_id="%1$s">%2$s</span>', $id, $remove_button_text );
|
302 |
-
}
|
303 |
-
|
304 |
-
printf( '<input id="%1$s" name="%2$s" type="hidden" value="%3$s" />', $id, $setting_name, $current );
|
305 |
-
|
306 |
-
printf( '<span class="button wpo_upload_image_button %4$s" data-uploader_title="%1$s" data-uploader_button_text="%2$s" data-remove_button_text="%3$s" data-input_id="%4$s">%2$s</span>', $uploader_title, $uploader_button_text, $remove_button_text, $id );
|
307 |
-
|
308 |
-
// Displays option description.
|
309 |
-
if ( isset( $description ) ) {
|
310 |
-
printf( '<p class="description">%s</p>', $description );
|
311 |
-
}
|
312 |
-
}
|
313 |
-
|
314 |
-
/**
|
315 |
-
* Next document number edit callback.
|
316 |
-
*
|
317 |
-
* @param array $args Field arguments.
|
318 |
-
*/
|
319 |
-
public function next_number_edit( $args ) {
|
320 |
-
extract( $args );
|
321 |
-
$number_store_method = WPO_WCPDF()->settings->get_sequential_number_store_method();
|
322 |
-
$number_store = new Sequential_Number_Store( $store, $number_store_method );
|
323 |
-
$next_number = $number_store->get_next();
|
324 |
-
$nonce = wp_create_nonce( "wpo_wcpdf_next_{$store}" );
|
325 |
-
printf( '<input id="next_%1$s" class="next-number-input" type="text" size="%2$s" value="%3$s" disabled="disabled" data-store="%1$s" data-nonce="%4$s"/> <span class="edit-next-number dashicons dashicons-edit"></span><span class="save-next-number button secondary" style="display:none;">%5$s</span>', $store, $size, $next_number, $nonce, __( 'Save', 'woocommerce-pdf-invoices-packing-slips' ) );
|
326 |
-
// Displays option description.
|
327 |
-
if ( isset( $description ) ) {
|
328 |
-
printf( '<p class="description">%s</p>', $description );
|
329 |
-
}
|
330 |
-
}
|
331 |
-
|
332 |
-
/**
|
333 |
-
* Wrapper function to create tabs for settings in different languages
|
334 |
-
* @param [type] $args [description]
|
335 |
-
* @param [type] $callback [description]
|
336 |
-
* @return [type] [description]
|
337 |
-
*/
|
338 |
-
public function i18n_wrap ( $args ) {
|
339 |
-
extract( $this->normalize_settings_args( $args ) );
|
340 |
-
|
341 |
-
if ( $languages = $this->get_languages() ) {
|
342 |
-
printf( '<div id="%s-%s-translations" class="translations">', $option_name, $id)
|
343 |
-
?>
|
344 |
-
<ul>
|
345 |
-
<?php foreach ( $languages as $lang_code => $language_name ) {
|
346 |
-
$translation_id = "{$option_name}_{$id}_{$lang_code}";
|
347 |
-
printf('<li><a href="#%s">%s</a></li>', $translation_id, $language_name );
|
348 |
-
}
|
349 |
-
?>
|
350 |
-
</ul>
|
351 |
-
<?php foreach ( $languages as $lang_code => $language_name ) {
|
352 |
-
$translation_id = "{$option_name}_{$id}_{$lang_code}";
|
353 |
-
printf( '<div id="%s">', $translation_id );
|
354 |
-
$args['lang'] = $lang_code;
|
355 |
-
// don't use internationalized placeholders since they're not translated,
|
356 |
-
// to avoid confusion (user thinking they're all the same)
|
357 |
-
if ( $callback == 'multiple_text_input' ) {
|
358 |
-
foreach ($fields as $key => $field_args) {
|
359 |
-
if (!empty($field_args['placeholder']) && isset($field_args['i18n_placeholder'])) {
|
360 |
-
$args['fields'][$key]['placeholder'] = '';
|
361 |
-
}
|
362 |
-
}
|
363 |
-
} else {
|
364 |
-
if (!empty($args['placeholder']) && isset($args['i18n_placeholder'])) {
|
365 |
-
$args['placeholder'] = '';
|
366 |
-
}
|
367 |
-
}
|
368 |
-
// specific description for internationalized fields (to compensate for missing placeholder)
|
369 |
-
if (!empty($args['i18n_description'])) {
|
370 |
-
$args['description'] = $args['i18n_description'];
|
371 |
-
}
|
372 |
-
call_user_func( array( $this, $callback ), $args );
|
373 |
-
echo '</div>';
|
374 |
-
}
|
375 |
-
?>
|
376 |
-
|
377 |
-
</div>
|
378 |
-
<?php
|
379 |
-
} else {
|
380 |
-
$args['lang'] = 'default';
|
381 |
-
call_user_func( array( $this, $callback ), $args );
|
382 |
-
}
|
383 |
-
}
|
384 |
-
|
385 |
-
public function get_languages () {
|
386 |
-
$multilingual = function_exists('icl_get_languages');
|
387 |
-
// $multilingual = true; // for development
|
388 |
-
|
389 |
-
if ($multilingual) {
|
390 |
-
// use this instead of function call for development outside of WPML
|
391 |
-
// $icl_get_languages = 'a:3:{s:2:"en";a:8:{s:2:"id";s:1:"1";s:6:"active";s:1:"1";s:11:"native_name";s:7:"English";s:7:"missing";s:1:"0";s:15:"translated_name";s:7:"English";s:13:"language_code";s:2:"en";s:16:"country_flag_url";s:43:"http://yourdomain/wpmlpath/res/flags/en.png";s:3:"url";s:23:"http://yourdomain/about";}s:2:"fr";a:8:{s:2:"id";s:1:"4";s:6:"active";s:1:"0";s:11:"native_name";s:9:"Français";s:7:"missing";s:1:"0";s:15:"translated_name";s:6:"French";s:13:"language_code";s:2:"fr";s:16:"country_flag_url";s:43:"http://yourdomain/wpmlpath/res/flags/fr.png";s:3:"url";s:29:"http://yourdomain/fr/a-propos";}s:2:"it";a:8:{s:2:"id";s:2:"27";s:6:"active";s:1:"0";s:11:"native_name";s:8:"Italiano";s:7:"missing";s:1:"0";s:15:"translated_name";s:7:"Italian";s:13:"language_code";s:2:"it";s:16:"country_flag_url";s:43:"http://yourdomain/wpmlpath/res/flags/it.png";s:3:"url";s:26:"http://yourdomain/it/circa";}}';
|
392 |
-
// $icl_get_languages = unserialize($icl_get_languages);
|
393 |
-
|
394 |
-
$icl_get_languages = icl_get_languages('skip_missing=0');
|
395 |
-
$languages = array();
|
396 |
-
foreach ($icl_get_languages as $lang => $data) {
|
397 |
-
$languages[$data['language_code']] = $data['native_name'];
|
398 |
-
}
|
399 |
-
} else {
|
400 |
-
return false;
|
401 |
-
}
|
402 |
-
|
403 |
-
return $languages;
|
404 |
-
}
|
405 |
-
|
406 |
-
public function normalize_settings_args ( $args ) {
|
407 |
-
$args['value'] = isset( $args['value'] ) ? $args['value'] : 1;
|
408 |
-
|
409 |
-
$args['placeholder'] = isset( $args['placeholder'] ) ? $args['placeholder'] : '';
|
410 |
-
|
411 |
-
// get main settings array
|
412 |
-
$option = get_option( $args['option_name'] );
|
413 |
-
|
414 |
-
$args['setting_name'] = "{$args['option_name']}[{$args['id']}]";
|
415 |
-
|
416 |
-
if ( !isset($args['lang']) && !empty($args['translatable']) ) {
|
417 |
-
$args['lang'] = 'default';
|
418 |
-
}
|
419 |
-
|
420 |
-
if (isset($args['lang'])) {
|
421 |
-
// i18n settings name
|
422 |
-
$args['setting_name'] = "{$args['setting_name']}[{$args['lang']}]";
|
423 |
-
// copy current option value if set
|
424 |
-
|
425 |
-
if ( $args['lang'] == 'default' && !empty($option[$args['id']]) && !isset( $option[$args['id']]['default'] ) ) {
|
426 |
-
// we're switching back from WPML to normal
|
427 |
-
// try english first
|
428 |
-
if ( isset( $option[$args['id']]['en'] ) ) {
|
429 |
-
$args['current'] = $option[$args['id']]['en'];
|
430 |
-
} elseif ( is_array( $option[$args['id']] ) ) {
|
431 |
-
// fallback to the first language if english not found
|
432 |
-
$first = array_shift($option[$args['id']]);
|
433 |
-
if (!empty($first)) {
|
434 |
-
$args['current'] = $first;
|
435 |
-
}
|
436 |
-
} elseif ( is_string( $option[$args['id']] ) ) {
|
437 |
-
$args['current'] = $option[$args['id']];
|
438 |
-
} else {
|
439 |
-
// nothing, really?
|
440 |
-
$args['current'] = '';
|
441 |
-
}
|
442 |
-
} else {
|
443 |
-
if ( isset( $option[$args['id']][$args['lang']] ) ) {
|
444 |
-
$args['current'] = $option[$args['id']][$args['lang']];
|
445 |
-
} elseif (isset( $option[$args['id']]['default'] )) {
|
446 |
-
$args['current'] = $option[$args['id']]['default'];
|
447 |
-
}
|
448 |
-
}
|
449 |
-
} else {
|
450 |
-
// copy current option value if set
|
451 |
-
if ( isset( $option[$args['id']] ) ) {
|
452 |
-
$args['current'] = $option[$args['id']];
|
453 |
-
}
|
454 |
-
}
|
455 |
-
|
456 |
-
// falback to default or empty if no value in option
|
457 |
-
if ( !isset($args['current']) ) {
|
458 |
-
$args['current'] = isset( $args['default'] ) ? $args['default'] : '';
|
459 |
-
}
|
460 |
-
|
461 |
-
return $args;
|
462 |
-
}
|
463 |
-
|
464 |
-
/**
|
465 |
-
* Validate options.
|
466 |
-
*
|
467 |
-
* @param array $input options to valid.
|
468 |
-
*
|
469 |
-
* @return array validated options.
|
470 |
-
*/
|
471 |
-
public function validate( $input ) {
|
472 |
-
// echo '<pre>';var_dump($input);die('</pre>');
|
473 |
-
// Create our array for storing the validated options.
|
474 |
-
$output = array();
|
475 |
-
|
476 |
-
if (empty($input) || !is_array($input)) {
|
477 |
-
return $input;
|
478 |
-
}
|
479 |
-
|
480 |
-
// Loop through each of the incoming options.
|
481 |
-
foreach ( $input as $key => $value ) {
|
482 |
-
|
483 |
-
// Check to see if the current option has a value. If so, process it.
|
484 |
-
if ( isset( $input[$key] ) ) {
|
485 |
-
if ( is_array( $input[$key] ) ) {
|
486 |
-
foreach ( $input[$key] as $sub_key => $sub_value ) {
|
487 |
-
$output[$key][$sub_key] = $input[$key][$sub_key];
|
488 |
-
}
|
489 |
-
} else {
|
490 |
-
$output[$key] = $input[$key];
|
491 |
-
}
|
492 |
-
}
|
493 |
-
}
|
494 |
-
|
495 |
-
// Return the array processing any additional functions filtered by this action.
|
496 |
-
return apply_filters( 'wpo_wcpdf_validate_input', $output, $input );
|
497 |
-
}
|
498 |
-
}
|
499 |
-
|
500 |
-
|
501 |
-
endif; // class_exists
|
502 |
-
|
503 |
return new Settings_Callbacks();
|
1 |
+
<?php
|
2 |
+
namespace WPO\WC\PDF_Invoices;
|
3 |
+
|
4 |
+
use WPO\WC\PDF_Invoices\Documents\Sequential_Number_Store;
|
5 |
+
|
6 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
7 |
+
exit; // Exit if accessed directly
|
8 |
+
}
|
9 |
+
|
10 |
+
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Settings_Callbacks' ) ) :
|
11 |
+
|
12 |
+
class Settings_Callbacks {
|
13 |
+
/**
|
14 |
+
* Section null callback.
|
15 |
+
*
|
16 |
+
* @return void.
|
17 |
+
*/
|
18 |
+
public function section() {
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Debug section callback.
|
23 |
+
*
|
24 |
+
* @return void.
|
25 |
+
*/
|
26 |
+
public function debug_section() {
|
27 |
+
_e( '<b>Warning!</b> The settings below are meant for debugging/development only. Do not use them on a live website!' , 'woocommerce-pdf-invoices-packing-slips' );
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Custom fields section callback.
|
32 |
+
*
|
33 |
+
* @return void.
|
34 |
+
*/
|
35 |
+
public function custom_fields_section() {
|
36 |
+
_e( 'These are used for the (optional) footer columns in the <em>Modern (Premium)</em> template, but can also be used for other elements in your custom template' , 'woocommerce-pdf-invoices-packing-slips' );
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Checkbox callback.
|
41 |
+
*
|
42 |
+
* args:
|
43 |
+
* option_name - name of the main option
|
44 |
+
* id - key of the setting
|
45 |
+
* value - value if not 1 (optional)
|
46 |
+
* default - default setting (optional)
|
47 |
+
* description - description (optional)
|
48 |
+
*
|
49 |
+
* @return void.
|
50 |
+
*/
|
51 |
+
public function checkbox( $args ) {
|
52 |
+
extract( $this->normalize_settings_args( $args ) );
|
53 |
+
|
54 |
+
// output checkbox
|
55 |
+
printf( '<input type="checkbox" id="%1$s" name="%2$s" value="%3$s"%4$s />', $id, $setting_name, $value, checked( $value, $current, false ) );
|
56 |
+
|
57 |
+
// output description.
|
58 |
+
if ( isset( $description ) ) {
|
59 |
+
printf( '<p class="description">%s</p>', $description );
|
60 |
+
}
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Text input callback.
|
65 |
+
*
|
66 |
+
* args:
|
67 |
+
* option_name - name of the main option
|
68 |
+
* id - key of the setting
|
69 |
+
* size - size of the text input (em)
|
70 |
+
* default - default setting (optional)
|
71 |
+
* description - description (optional)
|
72 |
+
* type - type (optional)
|
73 |
+
*
|
74 |
+
* @return void.
|
75 |
+
*/
|
76 |
+
public function text_input( $args ) {
|
77 |
+
extract( $this->normalize_settings_args( $args ) );
|
78 |
+
|
79 |
+
if (empty($type)) {
|
80 |
+
$type = 'text';
|
81 |
+
}
|
82 |
+
|
83 |
+
printf( '<input type="%1$s" id="%2$s" name="%3$s" value="%4$s" size="%5$s" placeholder="%6$s"/>', $type, $id, $setting_name, esc_attr( $current ), $size, $placeholder );
|
84 |
+
|
85 |
+
// output description.
|
86 |
+
if ( isset( $description ) ) {
|
87 |
+
printf( '<p class="description">%s</p>', $description );
|
88 |
+
}
|
89 |
+
}
|
90 |
+
|
91 |
+
// Single text option (not part of any settings array)
|
92 |
+
public function singular_text_element( $args ) {
|
93 |
+
$option_name = $args['option_name'];
|
94 |
+
$id = $args['id'];
|
95 |
+
$size = isset( $args['size'] ) ? $args['size'] : '25';
|
96 |
+
$class = isset( $args['translatable'] ) && $args['translatable'] === true ? 'translatable' : '';
|
97 |
+
|
98 |
+
$option = get_option( $option_name );
|
99 |
+
|
100 |
+
if ( isset( $option ) ) {
|
101 |
+
$current = $option;
|
102 |
+
} else {
|
103 |
+
$current = isset( $args['default'] ) ? $args['default'] : '';
|
104 |
+
}
|
105 |
+
|
106 |
+
$html = sprintf( '<input type="text" id="%1$s" name="%2$s" value="%3$s" size="%4$s" class="%5$s"/>', $id, $option_name, $current, $size, $class );
|
107 |
+
|
108 |
+
// Displays option description.
|
109 |
+
if ( isset( $args['description'] ) ) {
|
110 |
+
$html .= sprintf( '<p class="description">%s</p>', $args['description'] );
|
111 |
+
}
|
112 |
+
|
113 |
+
echo $html;
|
114 |
+
}
|
115 |
+
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Textarea callback.
|
119 |
+
*
|
120 |
+
* args:
|
121 |
+
* option_name - name of the main option
|
122 |
+
* id - key of the setting
|
123 |
+
* width - width of the text input (em)
|
124 |
+
* height - height of the text input (lines)
|
125 |
+
* default - default setting (optional)
|
126 |
+
* description - description (optional)
|
127 |
+
*
|
128 |
+
* @return void.
|
129 |
+
*/
|
130 |
+
public function textarea( $args ) {
|
131 |
+
extract( $this->normalize_settings_args( $args ) );
|
132 |
+
|
133 |
+
printf( '<textarea id="%1$s" name="%2$s" cols="%4$s" rows="%5$s" placeholder="%6$s"/>%3$s</textarea>', $id, $setting_name, esc_textarea( $current ), $width, $height, $placeholder );
|
134 |
+
|
135 |
+
// output description.
|
136 |
+
if ( isset( $description ) ) {
|
137 |
+
printf( '<p class="description">%s</p>', $description );
|
138 |
+
}
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Select element callback.
|
143 |
+
*
|
144 |
+
* @param array $args Field arguments.
|
145 |
+
*
|
146 |
+
* @return string Select field.
|
147 |
+
*/
|
148 |
+
public function select( $args ) {
|
149 |
+
extract( $this->normalize_settings_args( $args ) );
|
150 |
+
|
151 |
+
printf( '<select id="%1$s" name="%2$s">', $id, $setting_name );
|
152 |
+
|
153 |
+
foreach ( $options as $key => $label ) {
|
154 |
+
printf( '<option value="%s"%s>%s</option>', $key, selected( $current, $key, false ), $label );
|
155 |
+
}
|
156 |
+
|
157 |
+
echo '</select>';
|
158 |
+
|
159 |
+
if (isset($custom)) {
|
160 |
+
printf( '<div class="%1$s_custom custom">', $id );
|
161 |
+
|
162 |
+
if (is_callable( array( $this, $custom['type'] ) ) ) {
|
163 |
+
$this->{$custom['type']}( $custom['args'] );
|
164 |
+
}
|
165 |
+
echo '</div>';
|
166 |
+
?>
|
167 |
+
<script type="text/javascript">
|
168 |
+
jQuery(document).ready(function($) {
|
169 |
+
function check_<?php echo $id; ?>_custom() {
|
170 |
+
var custom = $('#<?php echo $id; ?>').val();
|
171 |
+
if (custom == 'custom') {
|
172 |
+
$( '.<?php echo $id; ?>_custom').show();
|
173 |
+
} else {
|
174 |
+
$( '.<?php echo $id; ?>_custom').hide();
|
175 |
+
}
|
176 |
+
}
|
177 |
+
|
178 |
+
check_<?php echo $id; ?>_custom();
|
179 |
+
|
180 |
+
$( '#<?php echo $id; ?>' ).change(function() {
|
181 |
+
check_<?php echo $id; ?>_custom();
|
182 |
+
});
|
183 |
+
|
184 |
+
});
|
185 |
+
</script>
|
186 |
+
<?php
|
187 |
+
}
|
188 |
+
|
189 |
+
// Displays option description.
|
190 |
+
if ( isset( $args['description'] ) ) {
|
191 |
+
printf( '<p class="description">%s</p>', $args['description'] );
|
192 |
+
}
|
193 |
+
|
194 |
+
}
|
195 |
+
|
196 |
+
public function radio_button( $args ) {
|
197 |
+
extract( $this->normalize_settings_args( $args ) );
|
198 |
+
|
199 |
+
foreach ( $options as $key => $label ) {
|
200 |
+
printf( '<input type="radio" class="radio" id="%1$s[%3$s]" name="%2$s" value="%3$s"%4$s />', $id, $setting_name, $key, checked( $current, $key, false ) );
|
201 |
+
printf( '<label for="%1$s[%3$s]"> %4$s</label><br>', $id, $setting_name, $key, $label);
|
202 |
+
}
|
203 |
+
|
204 |
+
|
205 |
+
// Displays option description.
|
206 |
+
if ( isset( $args['description'] ) ) {
|
207 |
+
printf( '<p class="description">%s</p>', $args['description'] );
|
208 |
+
}
|
209 |
+
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* Multiple text element callback.
|
214 |
+
* @param array $args Field arguments.
|
215 |
+
* @return string Text input field.
|
216 |
+
*/
|
217 |
+
public function multiple_text_input( $args ) {
|
218 |
+
extract( $this->normalize_settings_args( $args ) );
|
219 |
+
|
220 |
+
if (!empty($header)) {
|
221 |
+
echo "<p><strong>{$header}</strong>:</p>";
|
222 |
+
}
|
223 |
+
|
224 |
+
printf('<p class="%s multiple-text-input">', $id);
|
225 |
+
foreach ($fields as $name => $field) {
|
226 |
+
$size = $field['size'];
|
227 |
+
$placeholder = isset( $field['placeholder'] ) ? $field['placeholder'] : '';
|
228 |
+
|
229 |
+
if (isset($field['label_width'])) {
|
230 |
+
$style = sprintf( 'style="display:inline-block; width:%1$s;"', $field['label_width'] );
|
231 |
+
} else {
|
232 |
+
$style = '';
|
233 |
+
}
|
234 |
+
|
235 |
+
$description = isset( $field['description'] ) ? '<span style="font-style:italic;">'.$field['description'].'</span>' : '';
|
236 |
+
|
237 |
+
// output field label
|
238 |
+
if (isset($field['label'])) {
|
239 |
+
printf( '<label for="%1$s_%2$s" %3$s>%4$s:</label>', $id, $name, $style, $field['label'] );
|
240 |
+
}
|
241 |
+
|
242 |
+
// output field
|
243 |
+
$field_current = isset($current[$name]) ? $current[$name] : '';
|
244 |
+
$type = isset( $field['type'] ) ? $field['type'] : 'text';
|
245 |
+
printf( '<input type="%1$s" id="%2$s_%4$s" name="%3$s[%4$s]" value="%5$s" size="%6$s" placeholder="%7$s"/> %8$s<br/>', $type, $id, $setting_name, $name, esc_attr( $field_current ), $size, $placeholder, $description );
|
246 |
+
}
|
247 |
+
echo "</p>";
|
248 |
+
|
249 |
+
// Displays option description.
|
250 |
+
if ( isset( $args['description'] ) ) {
|
251 |
+
printf( '<p class="description">%s</p>', $args['description'] );
|
252 |
+
}
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* Multiple text element callback.
|
257 |
+
* @param array $args Field arguments.
|
258 |
+
* @return string Text input field.
|
259 |
+
*/
|
260 |
+
public function multiple_checkboxes( $args ) {
|
261 |
+
extract( $this->normalize_settings_args( $args ) );
|
262 |
+
|
263 |
+
foreach ($fields as $name => $label) {
|
264 |
+
// $label = $field['label'];
|
265 |
+
|
266 |
+
// output checkbox
|
267 |
+
$field_current = isset($current[$name]) ? $current[$name] : '';
|
268 |
+
printf( '<input type="checkbox" id="%1$s_%3$s" name="%2$s[%3$s]" value="%4$s"%5$s />', $id, $setting_name, $name, $value, checked( $value, $field_current, false ) );
|
269 |
+
|
270 |
+
// output field label
|
271 |
+
printf( '<label for="%1$s_%2$s">%3$s</label><br>', $id, $name, $label );
|
272 |
+
|
273 |
+
}
|
274 |
+
|
275 |
+
// Displays option description.
|
276 |
+
if ( isset( $args['description'] ) ) {
|
277 |
+
printf( '<p class="description">%s</p>', $args['description'] );
|
278 |
+
}
|
279 |
+
}
|
280 |
+
|
281 |
+
/**
|
282 |
+
* Media upload callback.
|
283 |
+
*
|
284 |
+
* @param array $args Field arguments.
|
285 |
+
*
|
286 |
+
* @return string Media upload button & preview.
|
287 |
+
*/
|
288 |
+
public function media_upload( $args ) {
|
289 |
+
extract( $this->normalize_settings_args( $args ) );
|
290 |
+
|
291 |
+
if( !empty($current) ) {
|
292 |
+
$attachment = wp_get_attachment_image_src( $current, 'full', false );
|
293 |
+
|
294 |
+
$attachment_src = $attachment[0];
|
295 |
+
$attachment_width = $attachment[1];
|
296 |
+
$attachment_height = $attachment[2];
|
297 |
+
$attachment_resolution = round($attachment_height/(3/2.54));
|
298 |
+
|
299 |
+
printf('<img src="%1$s" style="display:block" id="img-%4$s"/>', $attachment_src, $attachment_width, $attachment_height, $id );
|
300 |
+
printf('<div class="attachment-resolution"><p class="description">%s: %sdpi (default height = 3cm)</p></div>', __('Image resolution'), $attachment_resolution );
|
301 |
+
printf('<span class="button wpo_remove_image_button" data-input_id="%1$s">%2$s</span>', $id, $remove_button_text );
|
302 |
+
}
|
303 |
+
|
304 |
+
printf( '<input id="%1$s" name="%2$s" type="hidden" value="%3$s" />', $id, $setting_name, $current );
|
305 |
+
|
306 |
+
printf( '<span class="button wpo_upload_image_button %4$s" data-uploader_title="%1$s" data-uploader_button_text="%2$s" data-remove_button_text="%3$s" data-input_id="%4$s">%2$s</span>', $uploader_title, $uploader_button_text, $remove_button_text, $id );
|
307 |
+
|
308 |
+
// Displays option description.
|
309 |
+
if ( isset( $description ) ) {
|
310 |
+
printf( '<p class="description">%s</p>', $description );
|
311 |
+
}
|
312 |
+
}
|
313 |
+
|
314 |
+
/**
|
315 |
+
* Next document number edit callback.
|
316 |
+
*
|
317 |
+
* @param array $args Field arguments.
|
318 |
+
*/
|
319 |
+
public function next_number_edit( $args ) {
|
320 |
+
extract( $args );
|
321 |
+
$number_store_method = WPO_WCPDF()->settings->get_sequential_number_store_method();
|
322 |
+
$number_store = new Sequential_Number_Store( $store, $number_store_method );
|
323 |
+
$next_number = $number_store->get_next();
|
324 |
+
$nonce = wp_create_nonce( "wpo_wcpdf_next_{$store}" );
|
325 |
+
printf( '<input id="next_%1$s" class="next-number-input" type="text" size="%2$s" value="%3$s" disabled="disabled" data-store="%1$s" data-nonce="%4$s"/> <span class="edit-next-number dashicons dashicons-edit"></span><span class="save-next-number button secondary" style="display:none;">%5$s</span>', $store, $size, $next_number, $nonce, __( 'Save', 'woocommerce-pdf-invoices-packing-slips' ) );
|
326 |
+
// Displays option description.
|
327 |
+
if ( isset( $description ) ) {
|
328 |
+
printf( '<p class="description">%s</p>', $description );
|
329 |
+
}
|
330 |
+
}
|
331 |
+
|
332 |
+
/**
|
333 |
+
* Wrapper function to create tabs for settings in different languages
|
334 |
+
* @param [type] $args [description]
|
335 |
+
* @param [type] $callback [description]
|
336 |
+
* @return [type] [description]
|
337 |
+
*/
|
338 |
+
public function i18n_wrap ( $args ) {
|
339 |
+
extract( $this->normalize_settings_args( $args ) );
|
340 |
+
|
341 |
+
if ( $languages = $this->get_languages() ) {
|
342 |
+
printf( '<div id="%s-%s-translations" class="translations">', $option_name, $id)
|
343 |
+
?>
|
344 |
+
<ul>
|
345 |
+
<?php foreach ( $languages as $lang_code => $language_name ) {
|
346 |
+
$translation_id = "{$option_name}_{$id}_{$lang_code}";
|
347 |
+
printf('<li><a href="#%s">%s</a></li>', $translation_id, $language_name );
|
348 |
+
}
|
349 |
+
?>
|
350 |
+
</ul>
|
351 |
+
<?php foreach ( $languages as $lang_code => $language_name ) {
|
352 |
+
$translation_id = "{$option_name}_{$id}_{$lang_code}";
|
353 |
+
printf( '<div id="%s">', $translation_id );
|
354 |
+
$args['lang'] = $lang_code;
|
355 |
+
// don't use internationalized placeholders since they're not translated,
|
356 |
+
// to avoid confusion (user thinking they're all the same)
|
357 |
+
if ( $callback == 'multiple_text_input' ) {
|
358 |
+
foreach ($fields as $key => $field_args) {
|
359 |
+
if (!empty($field_args['placeholder']) && isset($field_args['i18n_placeholder'])) {
|
360 |
+
$args['fields'][$key]['placeholder'] = '';
|
361 |
+
}
|
362 |
+
}
|
363 |
+
} else {
|
364 |
+
if (!empty($args['placeholder']) && isset($args['i18n_placeholder'])) {
|
365 |
+
$args['placeholder'] = '';
|
366 |
+
}
|
367 |
+
}
|
368 |
+
// specific description for internationalized fields (to compensate for missing placeholder)
|
369 |
+
if (!empty($args['i18n_description'])) {
|
370 |
+
$args['description'] = $args['i18n_description'];
|
371 |
+
}
|
372 |
+
call_user_func( array( $this, $callback ), $args );
|
373 |
+
echo '</div>';
|
374 |
+
}
|
375 |
+
?>
|
376 |
+
|
377 |
+
</div>
|
378 |
+
<?php
|
379 |
+
} else {
|
380 |
+
$args['lang'] = 'default';
|
381 |
+
call_user_func( array( $this, $callback ), $args );
|
382 |
+
}
|
383 |
+
}
|
384 |
+
|
385 |
+
public function get_languages () {
|
386 |
+
$multilingual = function_exists('icl_get_languages');
|
387 |
+
// $multilingual = true; // for development
|
388 |
+
|
389 |
+
if ($multilingual) {
|
390 |
+
// use this instead of function call for development outside of WPML
|
391 |
+
// $icl_get_languages = 'a:3:{s:2:"en";a:8:{s:2:"id";s:1:"1";s:6:"active";s:1:"1";s:11:"native_name";s:7:"English";s:7:"missing";s:1:"0";s:15:"translated_name";s:7:"English";s:13:"language_code";s:2:"en";s:16:"country_flag_url";s:43:"http://yourdomain/wpmlpath/res/flags/en.png";s:3:"url";s:23:"http://yourdomain/about";}s:2:"fr";a:8:{s:2:"id";s:1:"4";s:6:"active";s:1:"0";s:11:"native_name";s:9:"Français";s:7:"missing";s:1:"0";s:15:"translated_name";s:6:"French";s:13:"language_code";s:2:"fr";s:16:"country_flag_url";s:43:"http://yourdomain/wpmlpath/res/flags/fr.png";s:3:"url";s:29:"http://yourdomain/fr/a-propos";}s:2:"it";a:8:{s:2:"id";s:2:"27";s:6:"active";s:1:"0";s:11:"native_name";s:8:"Italiano";s:7:"missing";s:1:"0";s:15:"translated_name";s:7:"Italian";s:13:"language_code";s:2:"it";s:16:"country_flag_url";s:43:"http://yourdomain/wpmlpath/res/flags/it.png";s:3:"url";s:26:"http://yourdomain/it/circa";}}';
|
392 |
+
// $icl_get_languages = unserialize($icl_get_languages);
|
393 |
+
|
394 |
+
$icl_get_languages = icl_get_languages('skip_missing=0');
|
395 |
+
$languages = array();
|
396 |
+
foreach ($icl_get_languages as $lang => $data) {
|
397 |
+
$languages[$data['language_code']] = $data['native_name'];
|
398 |
+
}
|
399 |
+
} else {
|
400 |
+
return false;
|
401 |
+
}
|
402 |
+
|
403 |
+
return $languages;
|
404 |
+
}
|
405 |
+
|
406 |
+
public function normalize_settings_args ( $args ) {
|
407 |
+
$args['value'] = isset( $args['value'] ) ? $args['value'] : 1;
|
408 |
+
|
409 |
+
$args['placeholder'] = isset( $args['placeholder'] ) ? $args['placeholder'] : '';
|
410 |
+
|
411 |
+
// get main settings array
|
412 |
+
$option = get_option( $args['option_name'] );
|
413 |
+
|
414 |
+
$args['setting_name'] = "{$args['option_name']}[{$args['id']}]";
|
415 |
+
|
416 |
+
if ( !isset($args['lang']) && !empty($args['translatable']) ) {
|
417 |
+
$args['lang'] = 'default';
|
418 |
+
}
|
419 |
+
|
420 |
+
if (isset($args['lang'])) {
|
421 |
+
// i18n settings name
|
422 |
+
$args['setting_name'] = "{$args['setting_name']}[{$args['lang']}]";
|
423 |
+
// copy current option value if set
|
424 |
+
|
425 |
+
if ( $args['lang'] == 'default' && !empty($option[$args['id']]) && !isset( $option[$args['id']]['default'] ) ) {
|
426 |
+
// we're switching back from WPML to normal
|
427 |
+
// try english first
|
428 |
+
if ( isset( $option[$args['id']]['en'] ) ) {
|
429 |
+
$args['current'] = $option[$args['id']]['en'];
|
430 |
+
} elseif ( is_array( $option[$args['id']] ) ) {
|
431 |
+
// fallback to the first language if english not found
|
432 |
+
$first = array_shift($option[$args['id']]);
|
433 |
+
if (!empty($first)) {
|
434 |
+
$args['current'] = $first;
|
435 |
+
}
|
436 |
+
} elseif ( is_string( $option[$args['id']] ) ) {
|
437 |
+
$args['current'] = $option[$args['id']];
|
438 |
+
} else {
|
439 |
+
// nothing, really?
|
440 |
+
$args['current'] = '';
|
441 |
+
}
|
442 |
+
} else {
|
443 |
+
if ( isset( $option[$args['id']][$args['lang']] ) ) {
|
444 |
+
$args['current'] = $option[$args['id']][$args['lang']];
|
445 |
+
} elseif (isset( $option[$args['id']]['default'] )) {
|
446 |
+
$args['current'] = $option[$args['id']]['default'];
|
447 |
+
}
|
448 |
+
}
|
449 |
+
} else {
|
450 |
+
// copy current option value if set
|
451 |
+
if ( isset( $option[$args['id']] ) ) {
|
452 |
+
$args['current'] = $option[$args['id']];
|
453 |
+
}
|
454 |
+
}
|
455 |
+
|
456 |
+
// falback to default or empty if no value in option
|
457 |
+
if ( !isset($args['current']) ) {
|
458 |
+
$args['current'] = isset( $args['default'] ) ? $args['default'] : '';
|
459 |
+
}
|
460 |
+
|
461 |
+
return $args;
|
462 |
+
}
|
463 |
+
|
464 |
+
/**
|
465 |
+
* Validate options.
|
466 |
+
*
|
467 |
+
* @param array $input options to valid.
|
468 |
+
*
|
469 |
+
* @return array validated options.
|
470 |
+
*/
|
471 |
+
public function validate( $input ) {
|
472 |
+
// echo '<pre>';var_dump($input);die('</pre>');
|
473 |
+
// Create our array for storing the validated options.
|
474 |
+
$output = array();
|
475 |
+
|
476 |
+
if (empty($input) || !is_array($input)) {
|
477 |
+
return $input;
|
478 |
+
}
|
479 |
+
|
480 |
+
// Loop through each of the incoming options.
|
481 |
+
foreach ( $input as $key => $value ) {
|
482 |
+
|
483 |
+
// Check to see if the current option has a value. If so, process it.
|
484 |
+
if ( isset( $input[$key] ) ) {
|
485 |
+
if ( is_array( $input[$key] ) ) {
|
486 |
+
foreach ( $input[$key] as $sub_key => $sub_value ) {
|
487 |
+
$output[$key][$sub_key] = $input[$key][$sub_key];
|
488 |
+
}
|
489 |
+
} else {
|
490 |
+
$output[$key] = $input[$key];
|
491 |
+
}
|
492 |
+
}
|
493 |
+
}
|
494 |
+
|
495 |
+
// Return the array processing any additional functions filtered by this action.
|
496 |
+
return apply_filters( 'wpo_wcpdf_validate_input', $output, $input );
|
497 |
+
}
|
498 |
+
}
|
499 |
+
|
500 |
+
|
501 |
+
endif; // class_exists
|
502 |
+
|
503 |
return new Settings_Callbacks();
|
includes/class-wcpdf-settings-debug.php
CHANGED
@@ -1,196 +1,196 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPO\WC\PDF_Invoices;
|
3 |
-
|
4 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
5 |
-
exit; // Exit if accessed directly
|
6 |
-
}
|
7 |
-
|
8 |
-
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Settings_Debug' ) ) :
|
9 |
-
|
10 |
-
class Settings_Debug {
|
11 |
-
|
12 |
-
function __construct() {
|
13 |
-
add_action( 'admin_init', array( $this, 'init_settings' ) );
|
14 |
-
add_action( 'wpo_wcpdf_settings_output_debug', array( $this, 'output' ), 10, 1 );
|
15 |
-
add_action( 'wpo_wcpdf_after_settings_page', array( $this, 'debug_tools' ), 10, 2 );
|
16 |
-
}
|
17 |
-
|
18 |
-
public function output( $section ) {
|
19 |
-
settings_fields( "wpo_wcpdf_settings_debug" );
|
20 |
-
do_settings_sections( "wpo_wcpdf_settings_debug" );
|
21 |
-
|
22 |
-
submit_button();
|
23 |
-
}
|
24 |
-
|
25 |
-
public function debug_tools( $tab, $section ) {
|
26 |
-
if ($tab !== 'debug') {
|
27 |
-
return;
|
28 |
-
}
|
29 |
-
?>
|
30 |
-
<form method="post">
|
31 |
-
<input type="hidden" name="wpo_wcpdf_debug_tools_action" value="install_fonts">
|
32 |
-
<input type="submit" name="submit" id="submit" class="button" value="<?php _e( 'Reinstall fonts', 'woocommerce-pdf-invoices-packing-slips' ); ?>">
|
33 |
-
<?php
|
34 |
-
if (isset($_POST['wpo_wcpdf_debug_tools_action']) && $_POST['wpo_wcpdf_debug_tools_action'] == 'install_fonts') {
|
35 |
-
$font_path = WPO_WCPDF()->main->get_tmp_path( 'fonts' );
|
36 |
-
|
37 |
-
// clear folder first
|
38 |
-
if ( function_exists("glob") && $files = glob( $font_path.'/*.*' ) ) {
|
39 |
-
$exclude_files = array( 'index.php', '.htaccess' );
|
40 |
-
foreach($files as $file) {
|
41 |
-
if( is_file($file) && !in_array( basename($file), $exclude_files ) ) {
|
42 |
-
unlink($file);
|
43 |
-
}
|
44 |
-
}
|
45 |
-
}
|
46 |
-
|
47 |
-
WPO_WCPDF()->main->copy_fonts( $font_path );
|
48 |
-
printf('<div class="notice notice-success"><p>%s</p></div>', __( 'Fonts reinstalled!', 'woocommerce-pdf-invoices-packing-slips' ) );
|
49 |
-
}
|
50 |
-
?>
|
51 |
-
</form>
|
52 |
-
<form method="post">
|
53 |
-
<input type="hidden" name="wpo_wcpdf_debug_tools_action" value="clear_tmp">
|
54 |
-
<input type="submit" name="submit" id="submit" class="button" value="<?php _e( 'Remove temporary files', 'woocommerce-pdf-invoices-packing-slips' ); ?>">
|
55 |
-
<?php
|
56 |
-
if (isset($_POST['wpo_wcpdf_debug_tools_action']) && $_POST['wpo_wcpdf_debug_tools_action'] == 'clear_tmp') {
|
57 |
-
$tmp_path = WPO_WCPDF()->main->get_tmp_path('attachments');
|
58 |
-
|
59 |
-
if ( !function_exists("glob") ) {
|
60 |
-
// glob is disabled
|
61 |
-
printf('<div class="notice notice-error"><p>%s<br><code>%s</code></p></div>', __( "Unable to read temporary folder contents!", 'woocommerce-pdf-invoices-packing-slips' ), $tmp_path);
|
62 |
-
} else {
|
63 |
-
$success = 0;
|
64 |
-
$error = 0;
|
65 |
-
if ( $files = glob($tmp_path.'*.pdf') ) { // get all pdf files
|
66 |
-
foreach($files as $file) {
|
67 |
-
if(is_file($file)) {
|
68 |
-
// delete file
|
69 |
-
if ( unlink($file) === true ) {
|
70 |
-
$success++;
|
71 |
-
} else {
|
72 |
-
$error++;
|
73 |
-
}
|
74 |
-
}
|
75 |
-
}
|
76 |
-
|
77 |
-
if ($error > 0) {
|
78 |
-
$message = sprintf( __( 'Unable to delete %d files! (deleted %d)', 'woocommerce-pdf-invoices-packing-slips' ), $error, $success);
|
79 |
-
printf('<div class="notice notice-error"><p>%s</p></div>', $message);
|
80 |
-
} else {
|
81 |
-
$message = sprintf( __( 'Successfully deleted %d files!', 'woocommerce-pdf-invoices-packing-slips' ), $success );
|
82 |
-
printf('<div class="notice notice-success"><p>%s</p></div>', $message);
|
83 |
-
}
|
84 |
-
} else {
|
85 |
-
printf('<div class="notice notice-success"><p>%s</p></div>', __( 'Nothing to delete!', 'woocommerce-pdf-invoices-packing-slips' ) );
|
86 |
-
}
|
87 |
-
}
|
88 |
-
}
|
89 |
-
?>
|
90 |
-
</form>
|
91 |
-
<form method="post">
|
92 |
-
<input type="hidden" name="wpo_wcpdf_debug_tools_action" value="delete_legacy_settings">
|
93 |
-
<input type="submit" name="submit" id="submit" class="button" value="<?php _e( 'Delete legacy (1.X) settings', 'woocommerce-pdf-invoices-packing-slips' ); ?>">
|
94 |
-
<?php
|
95 |
-
if (isset($_POST['wpo_wcpdf_debug_tools_action']) && $_POST['wpo_wcpdf_debug_tools_action'] == 'delete_legacy_settings') {
|
96 |
-
// delete options
|
97 |
-
delete_option( 'wpo_wcpdf_general_settings' );
|
98 |
-
delete_option( 'wpo_wcpdf_template_settings' );
|
99 |
-
delete_option( 'wpo_wcpdf_debug_settings' );
|
100 |
-
// and delete cache of these options, just in case...
|
101 |
-
wp_cache_delete( 'wpo_wcpdf_general_settings','options' );
|
102 |
-
wp_cache_delete( 'wpo_wcpdf_template_settings','options' );
|
103 |
-
wp_cache_delete( 'wpo_wcpdf_debug_settings','options' );
|
104 |
-
|
105 |
-
printf('<div class="notice notice-success"><p>%s</p></div>', __( 'Legacy settings deleted!', 'woocommerce-pdf-invoices-packing-slips' ) );
|
106 |
-
}
|
107 |
-
?>
|
108 |
-
</form>
|
109 |
-
<?php
|
110 |
-
include( WPO_WCPDF()->plugin_path() . '/includes/views/dompdf-status.php' );
|
111 |
-
}
|
112 |
-
|
113 |
-
public function init_settings() {
|
114 |
-
// Register settings.
|
115 |
-
$page = $option_group = $option_name = 'wpo_wcpdf_settings_debug';
|
116 |
-
|
117 |
-
$settings_fields = array(
|
118 |
-
array(
|
119 |
-
'type' => 'section',
|
120 |
-
'id' => 'debug_settings',
|
121 |
-
'title' => __( 'Debug settings', 'woocommerce-pdf-invoices-packing-slips' ),
|
122 |
-
'callback' => 'section',
|
123 |
-
),
|
124 |
-
array(
|
125 |
-
'type' => 'setting',
|
126 |
-
'id' => 'legacy_mode',
|
127 |
-
'title' => __( 'Legacy mode', 'woocommerce-pdf-invoices-packing-slips' ),
|
128 |
-
'callback' => 'checkbox',
|
129 |
-
'section' => 'debug_settings',
|
130 |
-
'args' => array(
|
131 |
-
'option_name' => $option_name,
|
132 |
-
'id' => 'legacy_mode',
|
133 |
-
'description' => __( "Legacy mode ensures compatibility with templates and filters from previous versions.", 'woocommerce-pdf-invoices-packing-slips' ),
|
134 |
-
)
|
135 |
-
),
|
136 |
-
array(
|
137 |
-
'type' => 'setting',
|
138 |
-
'id' => 'calculate_document_numbers',
|
139 |
-
'title' => __( 'Calculate document numbers (slow)', 'woocommerce-pdf-invoices-packing-slips' ),
|
140 |
-
'callback' => 'checkbox',
|
141 |
-
'section' => 'debug_settings',
|
142 |
-
'args' => array(
|
143 |
-
'option_name' => $option_name,
|
144 |
-
'id' => 'calculate_document_numbers',
|
145 |
-
'description' => __( "Document numbers (such as invoice numbers) are generated using AUTO_INCREMENT by default. Use this setting if your database auto increments with more than 1.", 'woocommerce-pdf-invoices-packing-slips' ),
|
146 |
-
)
|
147 |
-
),
|
148 |
-
array(
|
149 |
-
'type' => 'setting',
|
150 |
-
'id' => 'enable_debug',
|
151 |
-
'title' => __( 'Enable debug output', 'woocommerce-pdf-invoices-packing-slips' ),
|
152 |
-
'callback' => 'checkbox',
|
153 |
-
'section' => 'debug_settings',
|
154 |
-
'args' => array(
|
155 |
-
'option_name' => $option_name,
|
156 |
-
'id' => 'enable_debug',
|
157 |
-
'description' => __( "Enable this option to output plugin errors if you're getting a blank page or other PDF generation issues", 'woocommerce-pdf-invoices-packing-slips' ) . '<br>' .
|
158 |
-
__( '<b>Caution!</b> This setting may reveal errors (from other plugins) in other places on your site too, therefor this is not recommended to leave it enabled on live sites.', 'woocommerce-pdf-invoices-packing-slips' ),
|
159 |
-
)
|
160 |
-
),
|
161 |
-
array(
|
162 |
-
'type' => 'setting',
|
163 |
-
'id' => 'html_output',
|
164 |
-
'title' => __( 'Output to HTML', 'woocommerce-pdf-invoices-packing-slips' ),
|
165 |
-
'callback' => 'checkbox',
|
166 |
-
'section' => 'debug_settings',
|
167 |
-
'args' => array(
|
168 |
-
'option_name' => $option_name,
|
169 |
-
'id' => 'html_output',
|
170 |
-
'description' => __( 'Send the template output as HTML to the browser instead of creating a PDF.', 'woocommerce-pdf-invoices-packing-slips' ),
|
171 |
-
)
|
172 |
-
),
|
173 |
-
array(
|
174 |
-
'type' => 'setting',
|
175 |
-
'id' => 'use_html5_parser',
|
176 |
-
'title' => __( 'Use alternative HTML5 parser to parse HTML', 'woocommerce-pdf-invoices-packing-slips' ),
|
177 |
-
'callback' => 'checkbox',
|
178 |
-
'section' => 'debug_settings',
|
179 |
-
'args' => array(
|
180 |
-
'option_name' => $option_name,
|
181 |
-
'id' => 'use_html5_parser',
|
182 |
-
)
|
183 |
-
),
|
184 |
-
);
|
185 |
-
|
186 |
-
// allow plugins to alter settings fields
|
187 |
-
$settings_fields = apply_filters( 'wpo_wcpdf_settings_fields_debug', $settings_fields, $page, $option_group, $option_name );
|
188 |
-
WPO_WCPDF()->settings->add_settings_fields( $settings_fields, $page, $option_group, $option_name );
|
189 |
-
return;
|
190 |
-
}
|
191 |
-
|
192 |
-
}
|
193 |
-
|
194 |
-
endif; // class_exists
|
195 |
-
|
196 |
return new Settings_Debug();
|
1 |
+
<?php
|
2 |
+
namespace WPO\WC\PDF_Invoices;
|
3 |
+
|
4 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
5 |
+
exit; // Exit if accessed directly
|
6 |
+
}
|
7 |
+
|
8 |
+
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Settings_Debug' ) ) :
|
9 |
+
|
10 |
+
class Settings_Debug {
|
11 |
+
|
12 |
+
function __construct() {
|
13 |
+
add_action( 'admin_init', array( $this, 'init_settings' ) );
|
14 |
+
add_action( 'wpo_wcpdf_settings_output_debug', array( $this, 'output' ), 10, 1 );
|
15 |
+
add_action( 'wpo_wcpdf_after_settings_page', array( $this, 'debug_tools' ), 10, 2 );
|
16 |
+
}
|
17 |
+
|
18 |
+
public function output( $section ) {
|
19 |
+
settings_fields( "wpo_wcpdf_settings_debug" );
|
20 |
+
do_settings_sections( "wpo_wcpdf_settings_debug" );
|
21 |
+
|
22 |
+
submit_button();
|
23 |
+
}
|
24 |
+
|
25 |
+
public function debug_tools( $tab, $section ) {
|
26 |
+
if ($tab !== 'debug') {
|
27 |
+
return;
|
28 |
+
}
|
29 |
+
?>
|
30 |
+
<form method="post">
|
31 |
+
<input type="hidden" name="wpo_wcpdf_debug_tools_action" value="install_fonts">
|
32 |
+
<input type="submit" name="submit" id="submit" class="button" value="<?php _e( 'Reinstall fonts', 'woocommerce-pdf-invoices-packing-slips' ); ?>">
|
33 |
+
<?php
|
34 |
+
if (isset($_POST['wpo_wcpdf_debug_tools_action']) && $_POST['wpo_wcpdf_debug_tools_action'] == 'install_fonts') {
|
35 |
+
$font_path = WPO_WCPDF()->main->get_tmp_path( 'fonts' );
|
36 |
+
|
37 |
+
// clear folder first
|
38 |
+
if ( function_exists("glob") && $files = glob( $font_path.'/*.*' ) ) {
|
39 |
+
$exclude_files = array( 'index.php', '.htaccess' );
|
40 |
+
foreach($files as $file) {
|
41 |
+
if( is_file($file) && !in_array( basename($file), $exclude_files ) ) {
|
42 |
+
unlink($file);
|
43 |
+
}
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
WPO_WCPDF()->main->copy_fonts( $font_path );
|
48 |
+
printf('<div class="notice notice-success"><p>%s</p></div>', __( 'Fonts reinstalled!', 'woocommerce-pdf-invoices-packing-slips' ) );
|
49 |
+
}
|
50 |
+
?>
|
51 |
+
</form>
|
52 |
+
<form method="post">
|
53 |
+
<input type="hidden" name="wpo_wcpdf_debug_tools_action" value="clear_tmp">
|
54 |
+
<input type="submit" name="submit" id="submit" class="button" value="<?php _e( 'Remove temporary files', 'woocommerce-pdf-invoices-packing-slips' ); ?>">
|
55 |
+
<?php
|
56 |
+
if (isset($_POST['wpo_wcpdf_debug_tools_action']) && $_POST['wpo_wcpdf_debug_tools_action'] == 'clear_tmp') {
|
57 |
+
$tmp_path = WPO_WCPDF()->main->get_tmp_path('attachments');
|
58 |
+
|
59 |
+
if ( !function_exists("glob") ) {
|
60 |
+
// glob is disabled
|
61 |
+
printf('<div class="notice notice-error"><p>%s<br><code>%s</code></p></div>', __( "Unable to read temporary folder contents!", 'woocommerce-pdf-invoices-packing-slips' ), $tmp_path);
|
62 |
+
} else {
|
63 |
+
$success = 0;
|
64 |
+
$error = 0;
|
65 |
+
if ( $files = glob($tmp_path.'*.pdf') ) { // get all pdf files
|
66 |
+
foreach($files as $file) {
|
67 |
+
if(is_file($file)) {
|
68 |
+
// delete file
|
69 |
+
if ( unlink($file) === true ) {
|
70 |
+
$success++;
|
71 |
+
} else {
|
72 |
+
$error++;
|
73 |
+
}
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
if ($error > 0) {
|
78 |
+
$message = sprintf( __( 'Unable to delete %d files! (deleted %d)', 'woocommerce-pdf-invoices-packing-slips' ), $error, $success);
|
79 |
+
printf('<div class="notice notice-error"><p>%s</p></div>', $message);
|
80 |
+
} else {
|
81 |
+
$message = sprintf( __( 'Successfully deleted %d files!', 'woocommerce-pdf-invoices-packing-slips' ), $success );
|
82 |
+
printf('<div class="notice notice-success"><p>%s</p></div>', $message);
|
83 |
+
}
|
84 |
+
} else {
|
85 |
+
printf('<div class="notice notice-success"><p>%s</p></div>', __( 'Nothing to delete!', 'woocommerce-pdf-invoices-packing-slips' ) );
|
86 |
+
}
|
87 |
+
}
|
88 |
+
}
|
89 |
+
?>
|
90 |
+
</form>
|
91 |
+
<form method="post">
|
92 |
+
<input type="hidden" name="wpo_wcpdf_debug_tools_action" value="delete_legacy_settings">
|
93 |
+
<input type="submit" name="submit" id="submit" class="button" value="<?php _e( 'Delete legacy (1.X) settings', 'woocommerce-pdf-invoices-packing-slips' ); ?>">
|
94 |
+
<?php
|
95 |
+
if (isset($_POST['wpo_wcpdf_debug_tools_action']) && $_POST['wpo_wcpdf_debug_tools_action'] == 'delete_legacy_settings') {
|
96 |
+
// delete options
|
97 |
+
delete_option( 'wpo_wcpdf_general_settings' );
|
98 |
+
delete_option( 'wpo_wcpdf_template_settings' );
|
99 |
+
delete_option( 'wpo_wcpdf_debug_settings' );
|
100 |
+
// and delete cache of these options, just in case...
|
101 |
+
wp_cache_delete( 'wpo_wcpdf_general_settings','options' );
|
102 |
+
wp_cache_delete( 'wpo_wcpdf_template_settings','options' );
|
103 |
+
wp_cache_delete( 'wpo_wcpdf_debug_settings','options' );
|
104 |
+
|
105 |
+
printf('<div class="notice notice-success"><p>%s</p></div>', __( 'Legacy settings deleted!', 'woocommerce-pdf-invoices-packing-slips' ) );
|
106 |
+
}
|
107 |
+
?>
|
108 |
+
</form>
|
109 |
+
<?php
|
110 |
+
include( WPO_WCPDF()->plugin_path() . '/includes/views/dompdf-status.php' );
|
111 |
+
}
|
112 |
+
|
113 |
+
public function init_settings() {
|
114 |
+
// Register settings.
|
115 |
+
$page = $option_group = $option_name = 'wpo_wcpdf_settings_debug';
|
116 |
+
|
117 |
+
$settings_fields = array(
|
118 |
+
array(
|
119 |
+
'type' => 'section',
|
120 |
+
'id' => 'debug_settings',
|
121 |
+
'title' => __( 'Debug settings', 'woocommerce-pdf-invoices-packing-slips' ),
|
122 |
+
'callback' => 'section',
|
123 |
+
),
|
124 |
+
array(
|
125 |
+
'type' => 'setting',
|
126 |
+
'id' => 'legacy_mode',
|
127 |
+
'title' => __( 'Legacy mode', 'woocommerce-pdf-invoices-packing-slips' ),
|
128 |
+
'callback' => 'checkbox',
|
129 |
+
'section' => 'debug_settings',
|
130 |
+
'args' => array(
|
131 |
+
'option_name' => $option_name,
|
132 |
+
'id' => 'legacy_mode',
|
133 |
+
'description' => __( "Legacy mode ensures compatibility with templates and filters from previous versions.", 'woocommerce-pdf-invoices-packing-slips' ),
|
134 |
+
)
|
135 |
+
),
|
136 |
+
array(
|
137 |
+
'type' => 'setting',
|
138 |
+
'id' => 'calculate_document_numbers',
|
139 |
+
'title' => __( 'Calculate document numbers (slow)', 'woocommerce-pdf-invoices-packing-slips' ),
|
140 |
+
'callback' => 'checkbox',
|
141 |
+
'section' => 'debug_settings',
|
142 |
+
'args' => array(
|
143 |
+
'option_name' => $option_name,
|
144 |
+
'id' => 'calculate_document_numbers',
|
145 |
+
'description' => __( "Document numbers (such as invoice numbers) are generated using AUTO_INCREMENT by default. Use this setting if your database auto increments with more than 1.", 'woocommerce-pdf-invoices-packing-slips' ),
|
146 |
+
)
|
147 |
+
),
|
148 |
+
array(
|
149 |
+
'type' => 'setting',
|
150 |
+
'id' => 'enable_debug',
|
151 |
+
'title' => __( 'Enable debug output', 'woocommerce-pdf-invoices-packing-slips' ),
|
152 |
+
'callback' => 'checkbox',
|
153 |
+
'section' => 'debug_settings',
|
154 |
+
'args' => array(
|
155 |
+
'option_name' => $option_name,
|
156 |
+
'id' => 'enable_debug',
|
157 |
+
'description' => __( "Enable this option to output plugin errors if you're getting a blank page or other PDF generation issues", 'woocommerce-pdf-invoices-packing-slips' ) . '<br>' .
|
158 |
+
__( '<b>Caution!</b> This setting may reveal errors (from other plugins) in other places on your site too, therefor this is not recommended to leave it enabled on live sites.', 'woocommerce-pdf-invoices-packing-slips' ),
|
159 |
+
)
|
160 |
+
),
|
161 |
+
array(
|
162 |
+
'type' => 'setting',
|
163 |
+
'id' => 'html_output',
|
164 |
+
'title' => __( 'Output to HTML', 'woocommerce-pdf-invoices-packing-slips' ),
|
165 |
+
'callback' => 'checkbox',
|
166 |
+
'section' => 'debug_settings',
|
167 |
+
'args' => array(
|
168 |
+
'option_name' => $option_name,
|
169 |
+
'id' => 'html_output',
|
170 |
+
'description' => __( 'Send the template output as HTML to the browser instead of creating a PDF.', 'woocommerce-pdf-invoices-packing-slips' ),
|
171 |
+
)
|
172 |
+
),
|
173 |
+
array(
|
174 |
+
'type' => 'setting',
|
175 |
+
'id' => 'use_html5_parser',
|
176 |
+
'title' => __( 'Use alternative HTML5 parser to parse HTML', 'woocommerce-pdf-invoices-packing-slips' ),
|
177 |
+
'callback' => 'checkbox',
|
178 |
+
'section' => 'debug_settings',
|
179 |
+
'args' => array(
|
180 |
+
'option_name' => $option_name,
|
181 |
+
'id' => 'use_html5_parser',
|
182 |
+
)
|
183 |
+
),
|
184 |
+
);
|
185 |
+
|
186 |
+
// allow plugins to alter settings fields
|
187 |
+
$settings_fields = apply_filters( 'wpo_wcpdf_settings_fields_debug', $settings_fields, $page, $option_group, $option_name );
|
188 |
+
WPO_WCPDF()->settings->add_settings_fields( $settings_fields, $page, $option_group, $option_name );
|
189 |
+
return;
|
190 |
+
}
|
191 |
+
|
192 |
+
}
|
193 |
+
|
194 |
+
endif; // class_exists
|
195 |
+
|
196 |
return new Settings_Debug();
|
includes/class-wcpdf-settings-documents.php
CHANGED
@@ -22,6 +22,7 @@ class Settings_Documents {
|
|
22 |
}
|
23 |
|
24 |
public function output( $section ) {
|
|
|
25 |
if ( !empty( $section ) ) {
|
26 |
$documents = WPO_WCPDF()->documents->get_documents('all');
|
27 |
?>
|
22 |
}
|
23 |
|
24 |
public function output( $section ) {
|
25 |
+
$section = !empty($section) ? $section : 'invoice';
|
26 |
if ( !empty( $section ) ) {
|
27 |
$documents = WPO_WCPDF()->documents->get_documents('all');
|
28 |
?>
|
includes/class-wcpdf-settings-general.php
CHANGED
@@ -1,299 +1,299 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPO\WC\PDF_Invoices;
|
3 |
-
|
4 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
5 |
-
exit; // Exit if accessed directly
|
6 |
-
}
|
7 |
-
|
8 |
-
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Settings_General' ) ) :
|
9 |
-
|
10 |
-
class Settings_General {
|
11 |
-
|
12 |
-
function __construct() {
|
13 |
-
add_action( 'admin_init', array( $this, 'init_settings' ) );
|
14 |
-
add_action( 'wpo_wcpdf_settings_output_general', array( $this, 'output' ), 10, 1 );
|
15 |
-
add_action( 'wpo_wcpdf_before_settings', array( $this, 'attachment_settings_hint' ), 10, 2 );
|
16 |
-
}
|
17 |
-
|
18 |
-
public function output( $section ) {
|
19 |
-
settings_fields( "wpo_wcpdf_settings_general" );
|
20 |
-
do_settings_sections( "wpo_wcpdf_settings_general" );
|
21 |
-
|
22 |
-
submit_button();
|
23 |
-
}
|
24 |
-
|
25 |
-
public function init_settings() {
|
26 |
-
$page = $option_group = $option_name = 'wpo_wcpdf_settings_general';
|
27 |
-
|
28 |
-
$template_base_path = ( defined( 'WC_TEMPLATE_PATH' ) ? WC_TEMPLATE_PATH : $GLOBALS['woocommerce']->template_url );
|
29 |
-
$theme_template_path = get_stylesheet_directory() . '/' . $template_base_path;
|
30 |
-
$wp_content_dir = str_replace( ABSPATH, '', WP_CONTENT_DIR );
|
31 |
-
$theme_template_path = substr($theme_template_path, strpos($theme_template_path, $wp_content_dir)) . 'pdf/yourtemplate';
|
32 |
-
$plugin_template_path = "{$wp_content_dir}/plugins/woocommerce-pdf-invoices-packing-slips/templates/Simple";
|
33 |
-
|
34 |
-
$settings_fields = array(
|
35 |
-
array(
|
36 |
-
'type' => 'section',
|
37 |
-
'id' => 'general_settings',
|
38 |
-
'title' => __( 'General settings', 'woocommerce-pdf-invoices-packing-slips' ),
|
39 |
-
'callback' => 'section',
|
40 |
-
),
|
41 |
-
array(
|
42 |
-
'type' => 'setting',
|
43 |
-
'id' => 'download_display',
|
44 |
-
'title' => __( 'How do you want to view the PDF?', 'woocommerce-pdf-invoices-packing-slips' ),
|
45 |
-
'callback' => 'select',
|
46 |
-
'section' => 'general_settings',
|
47 |
-
'args' => array(
|
48 |
-
'option_name' => $option_name,
|
49 |
-
'id' => 'download_display',
|
50 |
-
'options' => array(
|
51 |
-
'download' => __( 'Download the PDF' , 'woocommerce-pdf-invoices-packing-slips' ),
|
52 |
-
'display' => __( 'Open the PDF in a new browser tab/window' , 'woocommerce-pdf-invoices-packing-slips' ),
|
53 |
-
),
|
54 |
-
)
|
55 |
-
),
|
56 |
-
array(
|
57 |
-
'type' => 'setting',
|
58 |
-
'id' => 'template_path',
|
59 |
-
'title' => __( 'Choose a template', 'woocommerce-pdf-invoices-packing-slips' ),
|
60 |
-
'callback' => 'select',
|
61 |
-
'section' => 'general_settings',
|
62 |
-
'args' => array(
|
63 |
-
'option_name' => $option_name,
|
64 |
-
'id' => 'template_path',
|
65 |
-
'options' => $this->find_templates(),
|
66 |
-
'description' => sprintf( __( 'Want to use your own template? Copy all the files from <code>%s</code> to your (child) theme in <code>%s</code> to customize them' , 'woocommerce-pdf-invoices-packing-slips' ), $plugin_template_path, $theme_template_path),
|
67 |
-
)
|
68 |
-
),
|
69 |
-
array(
|
70 |
-
'type' => 'setting',
|
71 |
-
'id' => 'paper_size',
|
72 |
-
'title' => __( 'Paper size', 'woocommerce-pdf-invoices-packing-slips' ),
|
73 |
-
'callback' => 'select',
|
74 |
-
'section' => 'general_settings',
|
75 |
-
'args' => array(
|
76 |
-
'option_name' => $option_name,
|
77 |
-
'id' => 'paper_size',
|
78 |
-
'options' => apply_filters( 'wpo_wcpdf_template_settings_paper_size', array(
|
79 |
-
'a4' => __( 'A4' , 'woocommerce-pdf-invoices-packing-slips' ),
|
80 |
-
'letter' => __( 'Letter' , 'woocommerce-pdf-invoices-packing-slips' ),
|
81 |
-
) ),
|
82 |
-
)
|
83 |
-
),
|
84 |
-
array(
|
85 |
-
'type' => 'setting',
|
86 |
-
'id' => 'currency_font',
|
87 |
-
'title' => __( 'Extended currency symbol support', 'woocommerce-pdf-invoices-packing-slips' ),
|
88 |
-
'callback' => 'checkbox',
|
89 |
-
'section' => 'general_settings',
|
90 |
-
'args' => array(
|
91 |
-
'option_name' => $option_name,
|
92 |
-
'id' => 'currency_font',
|
93 |
-
'description' => __( 'Enable this if your currency symbol is not displaying properly' , 'woocommerce-pdf-invoices-packing-slips' ),
|
94 |
-
)
|
95 |
-
),
|
96 |
-
array(
|
97 |
-
'type' => 'setting',
|
98 |
-
'id' => 'font_subsetting',
|
99 |
-
'title' => __( 'Enable font subsetting', 'woocommerce-pdf-invoices-packing-slips' ),
|
100 |
-
'callback' => 'checkbox',
|
101 |
-
'section' => 'general_settings',
|
102 |
-
'args' => array(
|
103 |
-
'option_name' => $option_name,
|
104 |
-
'id' => 'font_subsetting',
|
105 |
-
'description' => __( "Font subsetting can reduce file size by only including the characters that are used in the PDF, but limits the ability to edit PDF files later. Recommended if you're using an Asian font." , 'woocommerce-pdf-invoices-packing-slips' ),
|
106 |
-
)
|
107 |
-
),
|
108 |
-
array(
|
109 |
-
'type' => 'setting',
|
110 |
-
'id' => 'header_logo',
|
111 |
-
'title' => __( 'Shop header/logo', 'woocommerce-pdf-invoices-packing-slips' ),
|
112 |
-
'callback' => 'media_upload',
|
113 |
-
'section' => 'general_settings',
|
114 |
-
'args' => array(
|
115 |
-
'option_name' => $option_name,
|
116 |
-
'id' => 'header_logo',
|
117 |
-
'uploader_title' => __( 'Select or upload your invoice header/logo', 'woocommerce-pdf-invoices-packing-slips' ),
|
118 |
-
'uploader_button_text' => __( 'Set image', 'woocommerce-pdf-invoices-packing-slips' ),
|
119 |
-
'remove_button_text' => __( 'Remove image', 'woocommerce-pdf-invoices-packing-slips' ),
|
120 |
-
//'description' => __( '...', 'woocommerce-pdf-invoices-packing-slips' ),
|
121 |
-
)
|
122 |
-
),
|
123 |
-
array(
|
124 |
-
'type' => 'setting',
|
125 |
-
'id' => 'shop_name',
|
126 |
-
'title' => __( 'Shop Name', 'woocommerce-pdf-invoices-packing-slips' ),
|
127 |
-
'callback' => 'text_input',
|
128 |
-
'section' => 'general_settings',
|
129 |
-
'args' => array(
|
130 |
-
'option_name' => $option_name,
|
131 |
-
'id' => 'shop_name',
|
132 |
-
'size' => '72',
|
133 |
-
'translatable' => true,
|
134 |
-
)
|
135 |
-
),
|
136 |
-
array(
|
137 |
-
'type' => 'setting',
|
138 |
-
'id' => 'shop_address',
|
139 |
-
'title' => __( 'Shop Address', 'woocommerce-pdf-invoices-packing-slips' ),
|
140 |
-
'callback' => 'textarea',
|
141 |
-
'section' => 'general_settings',
|
142 |
-
'args' => array(
|
143 |
-
'option_name' => $option_name,
|
144 |
-
'id' => 'shop_address',
|
145 |
-
'width' => '72',
|
146 |
-
'height' => '8',
|
147 |
-
'translatable' => true,
|
148 |
-
//'description' => __( '...', 'woocommerce-pdf-invoices-packing-slips' ),
|
149 |
-
)
|
150 |
-
),
|
151 |
-
array(
|
152 |
-
'type' => 'setting',
|
153 |
-
'id' => 'footer',
|
154 |
-
'title' => __( 'Footer: terms & conditions, policies, etc.', 'woocommerce-pdf-invoices-packing-slips' ),
|
155 |
-
'callback' => 'textarea',
|
156 |
-
'section' => 'general_settings',
|
157 |
-
'args' => array(
|
158 |
-
'option_name' => $option_name,
|
159 |
-
'id' => 'footer',
|
160 |
-
'width' => '72',
|
161 |
-
'height' => '4',
|
162 |
-
'translatable' => true,
|
163 |
-
//'description' => __( '...', 'woocommerce-pdf-invoices-packing-slips' ),
|
164 |
-
)
|
165 |
-
),
|
166 |
-
array(
|
167 |
-
'type' => 'section',
|
168 |
-
'id' => 'extra_template_fields',
|
169 |
-
'title' => __( 'Extra template fields', 'woocommerce-pdf-invoices-packing-slips' ),
|
170 |
-
'callback' => 'custom_fields_section',
|
171 |
-
),
|
172 |
-
array(
|
173 |
-
'type' => 'setting',
|
174 |
-
'id' => 'extra_1',
|
175 |
-
'title' => __( 'Extra field 1', 'woocommerce-pdf-invoices-packing-slips' ),
|
176 |
-
'callback' => 'textarea',
|
177 |
-
'section' => 'extra_template_fields',
|
178 |
-
'args' => array(
|
179 |
-
'option_name' => $option_name,
|
180 |
-
'id' => 'extra_1',
|
181 |
-
'width' => '72',
|
182 |
-
'height' => '8',
|
183 |
-
'description' => __( 'This is footer column 1 in the <i>Modern (Premium)</i> template', 'woocommerce-pdf-invoices-packing-slips' ),
|
184 |
-
'translatable' => true,
|
185 |
-
)
|
186 |
-
),
|
187 |
-
array(
|
188 |
-
'type' => 'setting',
|
189 |
-
'id' => 'extra_2',
|
190 |
-
'title' => __( 'Extra field 2', 'woocommerce-pdf-invoices-packing-slips' ),
|
191 |
-
'callback' => 'textarea',
|
192 |
-
'section' => 'extra_template_fields',
|
193 |
-
'args' => array(
|
194 |
-
'option_name' => $option_name,
|
195 |
-
'id' => 'extra_2',
|
196 |
-
'width' => '72',
|
197 |
-
'height' => '8',
|
198 |
-
'description' => __( 'This is footer column 2 in the <i>Modern (Premium)</i> template', 'woocommerce-pdf-invoices-packing-slips' ),
|
199 |
-
'translatable' => true,
|
200 |
-
)
|
201 |
-
),
|
202 |
-
array(
|
203 |
-
'type' => 'setting',
|
204 |
-
'id' => 'extra_3',
|
205 |
-
'title' => __( 'Extra field 3', 'woocommerce-pdf-invoices-packing-slips' ),
|
206 |
-
'callback' => 'textarea',
|
207 |
-
'section' => 'extra_template_fields',
|
208 |
-
'args' => array(
|
209 |
-
'option_name' => $option_name,
|
210 |
-
'id' => 'extra_3',
|
211 |
-
'width' => '72',
|
212 |
-
'height' => '8',
|
213 |
-
'description' => __( 'This is footer column 3 in the <i>Modern (Premium)</i> template', 'woocommerce-pdf-invoices-packing-slips' ),
|
214 |
-
'translatable' => true,
|
215 |
-
)
|
216 |
-
),
|
217 |
-
);
|
218 |
-
|
219 |
-
// allow plugins to alter settings fields
|
220 |
-
$settings_fields = apply_filters( 'wpo_wcpdf_settings_fields_general', $settings_fields, $page, $option_group, $option_name );
|
221 |
-
WPO_WCPDF()->settings->add_settings_fields( $settings_fields, $page, $option_group, $option_name );
|
222 |
-
return;
|
223 |
-
}
|
224 |
-
|
225 |
-
public function attachment_settings_hint( $active_tab, $active_section ) {
|
226 |
-
// save or check option to hide attachments settings hint
|
227 |
-
if ( isset( $_GET['wpo_wcpdf_hide_attachments_hint'] ) ) {
|
228 |
-
update_option( 'wpo_wcpdf_hide_attachments_hint', true );
|
229 |
-
$hide_hint = true;
|
230 |
-
} else {
|
231 |
-
$hide_hint = get_option( 'wpo_wcpdf_hide_attachments_hint' );
|
232 |
-
}
|
233 |
-
|
234 |
-
if ( $active_tab == 'general' && !$hide_hint ) {
|
235 |
-
$documents = WPO_WCPDF()->documents->get_documents();
|
236 |
-
|
237 |
-
foreach ($documents as $document) {
|
238 |
-
if ( $document->get_type() == 'invoice' ) {
|
239 |
-
$invoice_email_ids = $document->get_attach_to_email_ids();
|
240 |
-
if (empty($invoice_email_ids)) {
|
241 |
-
include_once( WPO_WCPDF()->plugin_path() . '/includes/views/attachment-settings-hint.php' );
|
242 |
-
}
|
243 |
-
}
|
244 |
-
}
|
245 |
-
}
|
246 |
-
}
|
247 |
-
|
248 |
-
/**
|
249 |
-
* List templates in plugin folder, theme folder & child theme folder
|
250 |
-
* @return array template path => template name
|
251 |
-
*/
|
252 |
-
public function find_templates() {
|
253 |
-
$installed_templates = array();
|
254 |
-
|
255 |
-
// get base paths
|
256 |
-
$template_base_path = ( defined( 'WC_TEMPLATE_PATH' ) ? WC_TEMPLATE_PATH : $GLOBALS['woocommerce']->template_url );
|
257 |
-
$template_base_path = untrailingslashit( $template_base_path );
|
258 |
-
$template_paths = array (
|
259 |
-
// note the order: child-theme before theme, so that array_unique filters out parent doubles
|
260 |
-
'default' => WPO_WCPDF()->plugin_path() . '/templates/',
|
261 |
-
'child-theme' => get_stylesheet_directory() . "/{$template_base_path}/pdf/",
|
262 |
-
'theme' => get_template_directory() . "/{$template_base_path}/pdf/",
|
263 |
-
);
|
264 |
-
|
265 |
-
$template_paths = apply_filters( 'wpo_wcpdf_template_paths', $template_paths );
|
266 |
-
|
267 |
-
if ( defined('WP_CONTENT_DIR') && strpos( WP_CONTENT_DIR, ABSPATH ) !== false ) {
|
268 |
-
$forwardslash_basepath = str_replace('\\','/', ABSPATH);
|
269 |
-
} else {
|
270 |
-
$forwardslash_basepath = str_replace('\\','/', WP_CONTENT_DIR);
|
271 |
-
}
|
272 |
-
|
273 |
-
foreach ($template_paths as $template_source => $template_path) {
|
274 |
-
$dirs = (array) glob( $template_path . '*' , GLOB_ONLYDIR);
|
275 |
-
|
276 |
-
foreach ($dirs as $dir) {
|
277 |
-
// we're stripping abspath to make the plugin settings more portable
|
278 |
-
$forwardslash_dir = str_replace('\\','/', $dir);
|
279 |
-
$installed_templates[ str_replace( $forwardslash_basepath, '', $forwardslash_dir ) ] = basename($dir);
|
280 |
-
}
|
281 |
-
}
|
282 |
-
|
283 |
-
// remove parent doubles
|
284 |
-
$installed_templates = array_unique($installed_templates);
|
285 |
-
|
286 |
-
if (empty($installed_templates)) {
|
287 |
-
// fallback to Simple template for servers with glob() disabled
|
288 |
-
$simple_template_path = str_replace( ABSPATH, '', $template_paths['default'] . 'Simple' );
|
289 |
-
$installed_templates[$simple_template_path] = 'Simple';
|
290 |
-
}
|
291 |
-
|
292 |
-
return apply_filters( 'wpo_wcpdf_templates', $installed_templates );
|
293 |
-
}
|
294 |
-
|
295 |
-
}
|
296 |
-
|
297 |
-
endif; // class_exists
|
298 |
-
|
299 |
return new Settings_General();
|
1 |
+
<?php
|
2 |
+
namespace WPO\WC\PDF_Invoices;
|
3 |
+
|
4 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
5 |
+
exit; // Exit if accessed directly
|
6 |
+
}
|
7 |
+
|
8 |
+
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Settings_General' ) ) :
|
9 |
+
|
10 |
+
class Settings_General {
|
11 |
+
|
12 |
+
function __construct() {
|
13 |
+
add_action( 'admin_init', array( $this, 'init_settings' ) );
|
14 |
+
add_action( 'wpo_wcpdf_settings_output_general', array( $this, 'output' ), 10, 1 );
|
15 |
+
add_action( 'wpo_wcpdf_before_settings', array( $this, 'attachment_settings_hint' ), 10, 2 );
|
16 |
+
}
|
17 |
+
|
18 |
+
public function output( $section ) {
|
19 |
+
settings_fields( "wpo_wcpdf_settings_general" );
|
20 |
+
do_settings_sections( "wpo_wcpdf_settings_general" );
|
21 |
+
|
22 |
+
submit_button();
|
23 |
+
}
|
24 |
+
|
25 |
+
public function init_settings() {
|
26 |
+
$page = $option_group = $option_name = 'wpo_wcpdf_settings_general';
|
27 |
+
|
28 |
+
$template_base_path = ( defined( 'WC_TEMPLATE_PATH' ) ? WC_TEMPLATE_PATH : $GLOBALS['woocommerce']->template_url );
|
29 |
+
$theme_template_path = get_stylesheet_directory() . '/' . $template_base_path;
|
30 |
+
$wp_content_dir = str_replace( ABSPATH, '', WP_CONTENT_DIR );
|
31 |
+
$theme_template_path = substr($theme_template_path, strpos($theme_template_path, $wp_content_dir)) . 'pdf/yourtemplate';
|
32 |
+
$plugin_template_path = "{$wp_content_dir}/plugins/woocommerce-pdf-invoices-packing-slips/templates/Simple";
|
33 |
+
|
34 |
+
$settings_fields = array(
|
35 |
+
array(
|
36 |
+
'type' => 'section',
|
37 |
+
'id' => 'general_settings',
|
38 |
+
'title' => __( 'General settings', 'woocommerce-pdf-invoices-packing-slips' ),
|
39 |
+
'callback' => 'section',
|
40 |
+
),
|
41 |
+
array(
|
42 |
+
'type' => 'setting',
|
43 |
+
'id' => 'download_display',
|
44 |
+
'title' => __( 'How do you want to view the PDF?', 'woocommerce-pdf-invoices-packing-slips' ),
|
45 |
+
'callback' => 'select',
|
46 |
+
'section' => 'general_settings',
|
47 |
+
'args' => array(
|
48 |
+
'option_name' => $option_name,
|
49 |
+
'id' => 'download_display',
|
50 |
+
'options' => array(
|
51 |
+
'download' => __( 'Download the PDF' , 'woocommerce-pdf-invoices-packing-slips' ),
|
52 |
+
'display' => __( 'Open the PDF in a new browser tab/window' , 'woocommerce-pdf-invoices-packing-slips' ),
|
53 |
+
),
|
54 |
+
)
|
55 |
+
),
|
56 |
+
array(
|
57 |
+
'type' => 'setting',
|
58 |
+
'id' => 'template_path',
|
59 |
+
'title' => __( 'Choose a template', 'woocommerce-pdf-invoices-packing-slips' ),
|
60 |
+
'callback' => 'select',
|
61 |
+
'section' => 'general_settings',
|
62 |
+
'args' => array(
|
63 |
+
'option_name' => $option_name,
|
64 |
+
'id' => 'template_path',
|
65 |
+
'options' => $this->find_templates(),
|
66 |
+
'description' => sprintf( __( 'Want to use your own template? Copy all the files from <code>%s</code> to your (child) theme in <code>%s</code> to customize them' , 'woocommerce-pdf-invoices-packing-slips' ), $plugin_template_path, $theme_template_path),
|
67 |
+
)
|
68 |
+
),
|
69 |
+
array(
|
70 |
+
'type' => 'setting',
|
71 |
+
'id' => 'paper_size',
|
72 |
+
'title' => __( 'Paper size', 'woocommerce-pdf-invoices-packing-slips' ),
|
73 |
+
'callback' => 'select',
|
74 |
+
'section' => 'general_settings',
|
75 |
+
'args' => array(
|
76 |
+
'option_name' => $option_name,
|
77 |
+
'id' => 'paper_size',
|
78 |
+
'options' => apply_filters( 'wpo_wcpdf_template_settings_paper_size', array(
|
79 |
+
'a4' => __( 'A4' , 'woocommerce-pdf-invoices-packing-slips' ),
|
80 |
+
'letter' => __( 'Letter' , 'woocommerce-pdf-invoices-packing-slips' ),
|
81 |
+
) ),
|
82 |
+
)
|
83 |
+
),
|
84 |
+
array(
|
85 |
+
'type' => 'setting',
|
86 |
+
'id' => 'currency_font',
|
87 |
+
'title' => __( 'Extended currency symbol support', 'woocommerce-pdf-invoices-packing-slips' ),
|
88 |
+
'callback' => 'checkbox',
|
89 |
+
'section' => 'general_settings',
|
90 |
+
'args' => array(
|
91 |
+
'option_name' => $option_name,
|
92 |
+
'id' => 'currency_font',
|
93 |
+
'description' => __( 'Enable this if your currency symbol is not displaying properly' , 'woocommerce-pdf-invoices-packing-slips' ),
|
94 |
+
)
|
95 |
+
),
|
96 |
+
array(
|
97 |
+
'type' => 'setting',
|
98 |
+
'id' => 'font_subsetting',
|
99 |
+
'title' => __( 'Enable font subsetting', 'woocommerce-pdf-invoices-packing-slips' ),
|
100 |
+
'callback' => 'checkbox',
|
101 |
+
'section' => 'general_settings',
|
102 |
+
'args' => array(
|
103 |
+
'option_name' => $option_name,
|
104 |
+
'id' => 'font_subsetting',
|
105 |
+
'description' => __( "Font subsetting can reduce file size by only including the characters that are used in the PDF, but limits the ability to edit PDF files later. Recommended if you're using an Asian font." , 'woocommerce-pdf-invoices-packing-slips' ),
|
106 |
+
)
|
107 |
+
),
|
108 |
+
array(
|
109 |
+
'type' => 'setting',
|
110 |
+
'id' => 'header_logo',
|
111 |
+
'title' => __( 'Shop header/logo', 'woocommerce-pdf-invoices-packing-slips' ),
|
112 |
+
'callback' => 'media_upload',
|
113 |
+
'section' => 'general_settings',
|
114 |
+
'args' => array(
|
115 |
+
'option_name' => $option_name,
|
116 |
+
'id' => 'header_logo',
|
117 |
+
'uploader_title' => __( 'Select or upload your invoice header/logo', 'woocommerce-pdf-invoices-packing-slips' ),
|
118 |
+
'uploader_button_text' => __( 'Set image', 'woocommerce-pdf-invoices-packing-slips' ),
|
119 |
+
'remove_button_text' => __( 'Remove image', 'woocommerce-pdf-invoices-packing-slips' ),
|
120 |
+
//'description' => __( '...', 'woocommerce-pdf-invoices-packing-slips' ),
|
121 |
+
)
|
122 |
+
),
|
123 |
+
array(
|
124 |
+
'type' => 'setting',
|
125 |
+
'id' => 'shop_name',
|
126 |
+
'title' => __( 'Shop Name', 'woocommerce-pdf-invoices-packing-slips' ),
|
127 |
+
'callback' => 'text_input',
|
128 |
+
'section' => 'general_settings',
|
129 |
+
'args' => array(
|
130 |
+
'option_name' => $option_name,
|
131 |
+
'id' => 'shop_name',
|
132 |
+
'size' => '72',
|
133 |
+
'translatable' => true,
|
134 |
+
)
|
135 |
+
),
|
136 |
+
array(
|
137 |
+
'type' => 'setting',
|
138 |
+
'id' => 'shop_address',
|
139 |
+
'title' => __( 'Shop Address', 'woocommerce-pdf-invoices-packing-slips' ),
|
140 |
+
'callback' => 'textarea',
|
141 |
+
'section' => 'general_settings',
|
142 |
+
'args' => array(
|
143 |
+
'option_name' => $option_name,
|
144 |
+
'id' => 'shop_address',
|
145 |
+
'width' => '72',
|
146 |
+
'height' => '8',
|
147 |
+
'translatable' => true,
|
148 |
+
//'description' => __( '...', 'woocommerce-pdf-invoices-packing-slips' ),
|
149 |
+
)
|
150 |
+
),
|
151 |
+
array(
|
152 |
+
'type' => 'setting',
|
153 |
+
'id' => 'footer',
|
154 |
+
'title' => __( 'Footer: terms & conditions, policies, etc.', 'woocommerce-pdf-invoices-packing-slips' ),
|
155 |
+
'callback' => 'textarea',
|
156 |
+
'section' => 'general_settings',
|
157 |
+
'args' => array(
|
158 |
+
'option_name' => $option_name,
|
159 |
+
'id' => 'footer',
|
160 |
+
'width' => '72',
|
161 |
+
'height' => '4',
|
162 |
+
'translatable' => true,
|
163 |
+
//'description' => __( '...', 'woocommerce-pdf-invoices-packing-slips' ),
|
164 |
+
)
|
165 |
+
),
|
166 |
+
array(
|
167 |
+
'type' => 'section',
|
168 |
+
'id' => 'extra_template_fields',
|
169 |
+
'title' => __( 'Extra template fields', 'woocommerce-pdf-invoices-packing-slips' ),
|
170 |
+
'callback' => 'custom_fields_section',
|
171 |
+
),
|
172 |
+
array(
|
173 |
+
'type' => 'setting',
|
174 |
+
'id' => 'extra_1',
|
175 |
+
'title' => __( 'Extra field 1', 'woocommerce-pdf-invoices-packing-slips' ),
|
176 |
+
'callback' => 'textarea',
|
177 |
+
'section' => 'extra_template_fields',
|
178 |
+
'args' => array(
|
179 |
+
'option_name' => $option_name,
|
180 |
+
'id' => 'extra_1',
|
181 |
+
'width' => '72',
|
182 |
+
'height' => '8',
|
183 |
+
'description' => __( 'This is footer column 1 in the <i>Modern (Premium)</i> template', 'woocommerce-pdf-invoices-packing-slips' ),
|
184 |
+
'translatable' => true,
|
185 |
+
)
|
186 |
+
),
|
187 |
+
array(
|
188 |
+
'type' => 'setting',
|
189 |
+
'id' => 'extra_2',
|
190 |
+
'title' => __( 'Extra field 2', 'woocommerce-pdf-invoices-packing-slips' ),
|
191 |
+
'callback' => 'textarea',
|
192 |
+
'section' => 'extra_template_fields',
|
193 |
+
'args' => array(
|
194 |
+
'option_name' => $option_name,
|
195 |
+
'id' => 'extra_2',
|
196 |
+
'width' => '72',
|
197 |
+
'height' => '8',
|
198 |
+
'description' => __( 'This is footer column 2 in the <i>Modern (Premium)</i> template', 'woocommerce-pdf-invoices-packing-slips' ),
|
199 |
+
'translatable' => true,
|
200 |
+
)
|
201 |
+
),
|
202 |
+
array(
|
203 |
+
'type' => 'setting',
|
204 |
+
'id' => 'extra_3',
|
205 |
+
'title' => __( 'Extra field 3', 'woocommerce-pdf-invoices-packing-slips' ),
|
206 |
+
'callback' => 'textarea',
|
207 |
+
'section' => 'extra_template_fields',
|
208 |
+
'args' => array(
|
209 |
+
'option_name' => $option_name,
|
210 |
+
'id' => 'extra_3',
|
211 |
+
'width' => '72',
|
212 |
+
'height' => '8',
|
213 |
+
'description' => __( 'This is footer column 3 in the <i>Modern (Premium)</i> template', 'woocommerce-pdf-invoices-packing-slips' ),
|
214 |
+
'translatable' => true,
|
215 |
+
)
|
216 |
+
),
|
217 |
+
);
|
218 |
+
|
219 |
+
// allow plugins to alter settings fields
|
220 |
+
$settings_fields = apply_filters( 'wpo_wcpdf_settings_fields_general', $settings_fields, $page, $option_group, $option_name );
|
221 |
+
WPO_WCPDF()->settings->add_settings_fields( $settings_fields, $page, $option_group, $option_name );
|
222 |
+
return;
|
223 |
+
}
|
224 |
+
|
225 |
+
public function attachment_settings_hint( $active_tab, $active_section ) {
|
226 |
+
// save or check option to hide attachments settings hint
|
227 |
+
if ( isset( $_GET['wpo_wcpdf_hide_attachments_hint'] ) ) {
|
228 |
+
update_option( 'wpo_wcpdf_hide_attachments_hint', true );
|
229 |
+
$hide_hint = true;
|
230 |
+
} else {
|
231 |
+
$hide_hint = get_option( 'wpo_wcpdf_hide_attachments_hint' );
|
232 |
+
}
|
233 |
+
|
234 |
+
if ( $active_tab == 'general' && !$hide_hint ) {
|
235 |
+
$documents = WPO_WCPDF()->documents->get_documents();
|
236 |
+
|
237 |
+
foreach ($documents as $document) {
|
238 |
+
if ( $document->get_type() == 'invoice' ) {
|
239 |
+
$invoice_email_ids = $document->get_attach_to_email_ids();
|
240 |
+
if (empty($invoice_email_ids)) {
|
241 |
+
include_once( WPO_WCPDF()->plugin_path() . '/includes/views/attachment-settings-hint.php' );
|
242 |
+
}
|
243 |
+
}
|
244 |
+
}
|
245 |
+
}
|
246 |
+
}
|
247 |
+
|
248 |
+
/**
|
249 |
+
* List templates in plugin folder, theme folder & child theme folder
|
250 |
+
* @return array template path => template name
|
251 |
+
*/
|
252 |
+
public function find_templates() {
|
253 |
+
$installed_templates = array();
|
254 |
+
|
255 |
+
// get base paths
|
256 |
+
$template_base_path = ( defined( 'WC_TEMPLATE_PATH' ) ? WC_TEMPLATE_PATH : $GLOBALS['woocommerce']->template_url );
|
257 |
+
$template_base_path = untrailingslashit( $template_base_path );
|
258 |
+
$template_paths = array (
|
259 |
+
// note the order: child-theme before theme, so that array_unique filters out parent doubles
|
260 |
+
'default' => WPO_WCPDF()->plugin_path() . '/templates/',
|
261 |
+
'child-theme' => get_stylesheet_directory() . "/{$template_base_path}/pdf/",
|
262 |
+
'theme' => get_template_directory() . "/{$template_base_path}/pdf/",
|
263 |
+
);
|
264 |
+
|
265 |
+
$template_paths = apply_filters( 'wpo_wcpdf_template_paths', $template_paths );
|
266 |
+
|
267 |
+
if ( defined('WP_CONTENT_DIR') && strpos( WP_CONTENT_DIR, ABSPATH ) !== false ) {
|
268 |
+
$forwardslash_basepath = str_replace('\\','/', ABSPATH);
|
269 |
+
} else {
|
270 |
+
$forwardslash_basepath = str_replace('\\','/', WP_CONTENT_DIR);
|
271 |
+
}
|
272 |
+
|
273 |
+
foreach ($template_paths as $template_source => $template_path) {
|
274 |
+
$dirs = (array) glob( $template_path . '*' , GLOB_ONLYDIR);
|
275 |
+
|
276 |
+
foreach ($dirs as $dir) {
|
277 |
+
// we're stripping abspath to make the plugin settings more portable
|
278 |
+
$forwardslash_dir = str_replace('\\','/', $dir);
|
279 |
+
$installed_templates[ str_replace( $forwardslash_basepath, '', $forwardslash_dir ) ] = basename($dir);
|
280 |
+
}
|
281 |
+
}
|
282 |
+
|
283 |
+
// remove parent doubles
|
284 |
+
$installed_templates = array_unique($installed_templates);
|
285 |
+
|
286 |
+
if (empty($installed_templates)) {
|
287 |
+
// fallback to Simple template for servers with glob() disabled
|
288 |
+
$simple_template_path = str_replace( ABSPATH, '', $template_paths['default'] . 'Simple' );
|
289 |
+
$installed_templates[$simple_template_path] = 'Simple';
|
290 |
+
}
|
291 |
+
|
292 |
+
return apply_filters( 'wpo_wcpdf_templates', $installed_templates );
|
293 |
+
}
|
294 |
+
|
295 |
+
}
|
296 |
+
|
297 |
+
endif; // class_exists
|
298 |
+
|
299 |
return new Settings_General();
|
includes/class-wcpdf-settings.php
CHANGED
@@ -1,255 +1,255 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPO\WC\PDF_Invoices;
|
3 |
-
|
4 |
-
use WPO\WC\PDF_Invoices\Documents\Sequential_Number_Store;
|
5 |
-
|
6 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
7 |
-
exit; // Exit if accessed directly
|
8 |
-
}
|
9 |
-
|
10 |
-
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Settings' ) ) :
|
11 |
-
|
12 |
-
class Settings {
|
13 |
-
public $options_page_hook;
|
14 |
-
|
15 |
-
function __construct() {
|
16 |
-
$this->callbacks = include( 'class-wcpdf-settings-callbacks.php' );
|
17 |
-
|
18 |
-
// include settings classes
|
19 |
-
$this->general = include( 'class-wcpdf-settings-general.php' );
|
20 |
-
$this->documents = include( 'class-wcpdf-settings-documents.php' );
|
21 |
-
$this->debug = include( 'class-wcpdf-settings-debug.php' );
|
22 |
-
|
23 |
-
|
24 |
-
// Settings menu item
|
25 |
-
add_action( 'admin_menu', array( $this, 'menu' ) ); // Add menu.
|
26 |
-
// Links on plugin page
|
27 |
-
add_filter( 'plugin_action_links_'.WPO_WCPDF()->plugin_basename, array( $this, 'add_settings_link' ) );
|
28 |
-
add_filter( 'plugin_row_meta', array( $this, 'add_support_links' ), 10, 2 );
|
29 |
-
|
30 |
-
// settings capabilities
|
31 |
-
add_filter( 'option_page_capability_wpo_wcpdf_general_settings', array( $this, 'settings_capabilities' ) );
|
32 |
-
|
33 |
-
$this->general_settings = get_option('wpo_wcpdf_settings_general');
|
34 |
-
$this->debug_settings = get_option('wpo_wcpdf_settings_debug');
|
35 |
-
|
36 |
-
// admin notice for auto_increment_increment
|
37 |
-
// add_action( 'admin_notices', array( $this, 'check_auto_increment_increment') );
|
38 |
-
|
39 |
-
// AJAX set number store
|
40 |
-
add_action( 'wp_ajax_wpo_wcpdf_set_next_number', array($this, 'set_number_store' ));
|
41 |
-
}
|
42 |
-
|
43 |
-
public function menu() {
|
44 |
-
$parent_slug = 'woocommerce';
|
45 |
-
|
46 |
-
$this->options_page_hook = add_submenu_page(
|
47 |
-
$parent_slug,
|
48 |
-
__( 'PDF Invoices', 'woocommerce-pdf-invoices-packing-slips' ),
|
49 |
-
__( 'PDF Invoices', 'woocommerce-pdf-invoices-packing-slips' ),
|
50 |
-
'manage_woocommerce',
|
51 |
-
'wpo_wcpdf_options_page',
|
52 |
-
array( $this, 'settings_page' )
|
53 |
-
);
|
54 |
-
}
|
55 |
-
|
56 |
-
/**
|
57 |
-
* Add settings link to plugins page
|
58 |
-
*/
|
59 |
-
public function add_settings_link( $links ) {
|
60 |
-
$action_links = array(
|
61 |
-
'settings' => '<a href="admin.php?page=wpo_wcpdf_options_page">'. __( 'Settings', 'woocommerce' ) . '</a>',
|
62 |
-
);
|
63 |
-
|
64 |
-
return array_merge( $action_links, $links );
|
65 |
-
}
|
66 |
-
|
67 |
-
/**
|
68 |
-
* Add various support links to plugin page
|
69 |
-
* after meta (version, authors, site)
|
70 |
-
*/
|
71 |
-
public function add_support_links( $links, $file ) {
|
72 |
-
if ( $file == WPO_WCPDF()->plugin_basename ) {
|
73 |
-
$row_meta = array(
|
74 |
-
'docs' => '<a href="http://docs.wpovernight.com/woocommerce-pdf-invoices-packing-slips/" target="_blank" title="' . __( 'Documentation', 'woocommerce-pdf-invoices-packing-slips' ) . '">' . __( 'Documentation', 'woocommerce-pdf-invoices-packing-slips' ) . '</a>',
|
75 |
-
'support' => '<a href="https://wordpress.org/support/plugin/woocommerce-pdf-invoices-packing-slips" target="_blank" title="' . __( 'Support Forum', 'woocommerce-pdf-invoices-packing-slips' ) . '">' . __( 'Support Forum', 'woocommerce-pdf-invoices-packing-slips' ) . '</a>',
|
76 |
-
);
|
77 |
-
|
78 |
-
return array_merge( $links, $row_meta );
|
79 |
-
}
|
80 |
-
return (array) $links;
|
81 |
-
}
|
82 |
-
|
83 |
-
function check_auto_increment_increment() {
|
84 |
-
global $wpdb;
|
85 |
-
$row = $wpdb->get_row("SHOW VARIABLES LIKE 'auto_increment_increment'");
|
86 |
-
if ( !empty($row) && !empty($row->Value) && $row->Value != 1 ) {
|
87 |
-
$error = sprintf( __( "<strong>Warning!</strong> Your database has an AUTO_INCREMENT step size of %s, your invoice numbers may not be sequential. Enable the 'Calculate document numbers (slow)' setting in the Status tab to use an alternate method." , 'woocommerce-pdf-invoices-packing-slips' ), $row->Value );
|
88 |
-
printf( '<div class="error"><p>%s</p></div>', $error );
|
89 |
-
}
|
90 |
-
}
|
91 |
-
|
92 |
-
|
93 |
-
public function settings_page() {
|
94 |
-
$settings_tabs = apply_filters( 'wpo_wcpdf_settings_tabs', array (
|
95 |
-
'general' => __('General', 'woocommerce-pdf-invoices-packing-slips' ),
|
96 |
-
'documents' => __('Documents', 'woocommerce-pdf-invoices-packing-slips' ),
|
97 |
-
)
|
98 |
-
);
|
99 |
-
|
100 |
-
// add status tab last in row
|
101 |
-
$settings_tabs['debug'] = __('Status', 'woocommerce-pdf-invoices-packing-slips' );
|
102 |
-
|
103 |
-
$active_tab = isset( $_GET[ 'tab' ] ) ? sanitize_text_field( $_GET[ 'tab' ] ) : 'general';
|
104 |
-
$active_section = isset( $_GET[ 'section' ] ) ? sanitize_text_field( $_GET[ 'section' ] ) : '';
|
105 |
-
|
106 |
-
include('views/wcpdf-settings-page.php');
|
107 |
-
}
|
108 |
-
|
109 |
-
public function add_settings_fields( $settings_fields, $page, $option_group, $option_name ) {
|
110 |
-
foreach ( $settings_fields as $settings_field ) {
|
111 |
-
if (!isset($settings_field['callback'])) {
|
112 |
-
continue;
|
113 |
-
} elseif ( is_callable( array( $this->callbacks, $settings_field['callback'] ) ) ) {
|
114 |
-
$callback = array( $this->callbacks, $settings_field['callback'] );
|
115 |
-
} elseif ( is_callable( $settings_field['callback'] ) ) {
|
116 |
-
$callback = $settings_field['callback'];
|
117 |
-
} else {
|
118 |
-
continue;
|
119 |
-
}
|
120 |
-
|
121 |
-
if ( $settings_field['type'] == 'section' ) {
|
122 |
-
add_settings_section(
|
123 |
-
$settings_field['id'],
|
124 |
-
$settings_field['title'],
|
125 |
-
$callback,
|
126 |
-
$page
|
127 |
-
);
|
128 |
-
} else {
|
129 |
-
add_settings_field(
|
130 |
-
$settings_field['id'],
|
131 |
-
$settings_field['title'],
|
132 |
-
$callback,
|
133 |
-
$page,
|
134 |
-
$settings_field['section'],
|
135 |
-
$settings_field['args']
|
136 |
-
);
|
137 |
-
// register option separately for singular options
|
138 |
-
if (is_string($settings_field['callback']) && $settings_field['callback'] == 'singular_text_element') {
|
139 |
-
register_setting( $option_group, $settings_field['args']['option_name'], array( $this->callbacks, 'validate' ) );
|
140 |
-
}
|
141 |
-
}
|
142 |
-
}
|
143 |
-
// $page, $option_group & $option_name are all the same...
|
144 |
-
register_setting( $option_group, $option_name, array( $this->callbacks, 'validate' ) );
|
145 |
-
add_filter( 'option_page_capability_'.$page, array( $this, 'settings_capabilities' ) );
|
146 |
-
|
147 |
-
}
|
148 |
-
|
149 |
-
/**
|
150 |
-
* Set capability for settings page
|
151 |
-
*/
|
152 |
-
public function settings_capabilities() {
|
153 |
-
return 'manage_woocommerce';
|
154 |
-
}
|
155 |
-
|
156 |
-
public function get_common_document_settings() {
|
157 |
-
$common_settings = array(
|
158 |
-
'paper_size' => isset( $this->general_settings['paper_size'] ) ? $this->general_settings['paper_size'] : '',
|
159 |
-
'font_subsetting' => isset( $this->general_settings['font_subsetting'] ) || ( defined("DOMPDF_ENABLE_FONTSUBSETTING") && DOMPDF_ENABLE_FONTSUBSETTING === true ) ? true : false,
|
160 |
-
'header_logo' => isset( $this->general_settings['header_logo'] ) ? $this->general_settings['header_logo'] : '',
|
161 |
-
'shop_name' => isset( $this->general_settings['shop_name'] ) ? $this->general_settings['shop_name'] : '',
|
162 |
-
'shop_address' => isset( $this->general_settings['shop_address'] ) ? $this->general_settings['shop_address'] : '',
|
163 |
-
'footer' => isset( $this->general_settings['footer'] ) ? $this->general_settings['footer'] : '',
|
164 |
-
'extra_1' => isset( $this->general_settings['extra_1'] ) ? $this->general_settings['extra_1'] : '',
|
165 |
-
'extra_2' => isset( $this->general_settings['extra_2'] ) ? $this->general_settings['extra_2'] : '',
|
166 |
-
'extra_3' => isset( $this->general_settings['extra_3'] ) ? $this->general_settings['extra_3'] : '',
|
167 |
-
);
|
168 |
-
return $common_settings;
|
169 |
-
}
|
170 |
-
|
171 |
-
public function get_document_settings( $document_type ) {
|
172 |
-
$documents = WPO_WCPDF()->documents->get_documents('all');
|
173 |
-
foreach ($documents as $document) {
|
174 |
-
if ( $document->get_type() == $document_type ) {
|
175 |
-
return $document->settings;
|
176 |
-
}
|
177 |
-
}
|
178 |
-
return false;
|
179 |
-
}
|
180 |
-
|
181 |
-
public function get_output_format() {
|
182 |
-
if ( isset( $this->debug_settings['html_output'] ) ) {
|
183 |
-
$output_format = 'html';
|
184 |
-
} else {
|
185 |
-
$output_format = 'pdf';
|
186 |
-
}
|
187 |
-
return $output_format;
|
188 |
-
}
|
189 |
-
|
190 |
-
public function get_output_mode() {
|
191 |
-
if ( isset( WPO_WCPDF()->settings->general_settings['download_display'] ) ) {
|
192 |
-
switch ( WPO_WCPDF()->settings->general_settings['download_display'] ) {
|
193 |
-
case 'display':
|
194 |
-
$output_mode = 'inline';
|
195 |
-
break;
|
196 |
-
case 'download':
|
197 |
-
default:
|
198 |
-
$output_mode = 'download';
|
199 |
-
break;
|
200 |
-
}
|
201 |
-
} else {
|
202 |
-
$output_mode = 'download';
|
203 |
-
}
|
204 |
-
return $output_mode;
|
205 |
-
}
|
206 |
-
|
207 |
-
public function get_template_path( $document_type = NULL ) {
|
208 |
-
$template_path = isset( $this->general_settings['template_path'] )?$this->general_settings['template_path']:'';
|
209 |
-
// forward slash for consistency
|
210 |
-
$template_path = str_replace('\\','/', $template_path);
|
211 |
-
|
212 |
-
// add base path, checking if it's not already there
|
213 |
-
// alternative setups like Bedrock have WP_CONTENT_DIR & ABSPATH separated
|
214 |
-
if ( defined('WP_CONTENT_DIR') && strpos( WP_CONTENT_DIR, ABSPATH ) !== false ) {
|
215 |
-
$forwardslash_basepath = str_replace('\\','/', ABSPATH);
|
216 |
-
} else {
|
217 |
-
// bedrock e.a
|
218 |
-
$forwardslash_basepath = str_replace('\\','/', WP_CONTENT_DIR);
|
219 |
-
}
|
220 |
-
|
221 |
-
if ( strpos( $template_path, $forwardslash_basepath ) === false ) {
|
222 |
-
$template_path = $forwardslash_basepath . $template_path;
|
223 |
-
}
|
224 |
-
|
225 |
-
return $template_path;
|
226 |
-
}
|
227 |
-
|
228 |
-
public function set_number_store() {
|
229 |
-
check_ajax_referer( "wpo_wcpdf_next_{$_POST['store']}", 'security' );
|
230 |
-
$number = isset( $_POST['number'] ) ? (int) $_POST['number'] : 0;
|
231 |
-
$number_store_method = $this->get_sequential_number_store_method();
|
232 |
-
$number_store = new Sequential_Number_Store( $_POST['store'], $number_store_method );
|
233 |
-
$number_store->set_next( $number );
|
234 |
-
echo "next number ({$_POST['store']}) set to {$number}";
|
235 |
-
die();
|
236 |
-
}
|
237 |
-
|
238 |
-
public function get_sequential_number_store_method() {
|
239 |
-
global $wpdb;
|
240 |
-
$method = isset( $this->debug_settings['calculate_document_numbers'] ) ? 'calculate' : 'auto_increment';
|
241 |
-
|
242 |
-
// safety first - always use calculate when auto_increment_increment is not 1
|
243 |
-
$row = $wpdb->get_row("SHOW VARIABLES LIKE 'auto_increment_increment'");
|
244 |
-
if ( !empty($row) && !empty($row->Value) && $row->Value != 1 ) {
|
245 |
-
$method = 'calculate';
|
246 |
-
}
|
247 |
-
|
248 |
-
return $method;
|
249 |
-
}
|
250 |
-
|
251 |
-
}
|
252 |
-
|
253 |
-
endif; // class_exists
|
254 |
-
|
255 |
return new Settings();
|
1 |
+
<?php
|
2 |
+
namespace WPO\WC\PDF_Invoices;
|
3 |
+
|
4 |
+
use WPO\WC\PDF_Invoices\Documents\Sequential_Number_Store;
|
5 |
+
|
6 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
7 |
+
exit; // Exit if accessed directly
|
8 |
+
}
|
9 |
+
|
10 |
+
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Settings' ) ) :
|
11 |
+
|
12 |
+
class Settings {
|
13 |
+
public $options_page_hook;
|
14 |
+
|
15 |
+
function __construct() {
|
16 |
+
$this->callbacks = include( 'class-wcpdf-settings-callbacks.php' );
|
17 |
+
|
18 |
+
// include settings classes
|
19 |
+
$this->general = include( 'class-wcpdf-settings-general.php' );
|
20 |
+
$this->documents = include( 'class-wcpdf-settings-documents.php' );
|
21 |
+
$this->debug = include( 'class-wcpdf-settings-debug.php' );
|
22 |
+
|
23 |
+
|
24 |
+
// Settings menu item
|
25 |
+
add_action( 'admin_menu', array( $this, 'menu' ) ); // Add menu.
|
26 |
+
// Links on plugin page
|
27 |
+
add_filter( 'plugin_action_links_'.WPO_WCPDF()->plugin_basename, array( $this, 'add_settings_link' ) );
|
28 |
+
add_filter( 'plugin_row_meta', array( $this, 'add_support_links' ), 10, 2 );
|
29 |
+
|
30 |
+
// settings capabilities
|
31 |
+
add_filter( 'option_page_capability_wpo_wcpdf_general_settings', array( $this, 'settings_capabilities' ) );
|
32 |
+
|
33 |
+
$this->general_settings = get_option('wpo_wcpdf_settings_general');
|
34 |
+
$this->debug_settings = get_option('wpo_wcpdf_settings_debug');
|
35 |
+
|
36 |
+
// admin notice for auto_increment_increment
|
37 |
+
// add_action( 'admin_notices', array( $this, 'check_auto_increment_increment') );
|
38 |
+
|
39 |
+
// AJAX set number store
|
40 |
+
add_action( 'wp_ajax_wpo_wcpdf_set_next_number', array($this, 'set_number_store' ));
|
41 |
+
}
|
42 |
+
|
43 |
+
public function menu() {
|
44 |
+
$parent_slug = 'woocommerce';
|
45 |
+
|
46 |
+
$this->options_page_hook = add_submenu_page(
|
47 |
+
$parent_slug,
|
48 |
+
__( 'PDF Invoices', 'woocommerce-pdf-invoices-packing-slips' ),
|
49 |
+
__( 'PDF Invoices', 'woocommerce-pdf-invoices-packing-slips' ),
|
50 |
+
'manage_woocommerce',
|
51 |
+
'wpo_wcpdf_options_page',
|
52 |
+
array( $this, 'settings_page' )
|
53 |
+
);
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Add settings link to plugins page
|
58 |
+
*/
|
59 |
+
public function add_settings_link( $links ) {
|
60 |
+
$action_links = array(
|
61 |
+
'settings' => '<a href="admin.php?page=wpo_wcpdf_options_page">'. __( 'Settings', 'woocommerce' ) . '</a>',
|
62 |
+
);
|
63 |
+
|
64 |
+
return array_merge( $action_links, $links );
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Add various support links to plugin page
|
69 |
+
* after meta (version, authors, site)
|
70 |
+
*/
|
71 |
+
public function add_support_links( $links, $file ) {
|
72 |
+
if ( $file == WPO_WCPDF()->plugin_basename ) {
|
73 |
+
$row_meta = array(
|
74 |
+
'docs' => '<a href="http://docs.wpovernight.com/woocommerce-pdf-invoices-packing-slips/" target="_blank" title="' . __( 'Documentation', 'woocommerce-pdf-invoices-packing-slips' ) . '">' . __( 'Documentation', 'woocommerce-pdf-invoices-packing-slips' ) . '</a>',
|
75 |
+
'support' => '<a href="https://wordpress.org/support/plugin/woocommerce-pdf-invoices-packing-slips" target="_blank" title="' . __( 'Support Forum', 'woocommerce-pdf-invoices-packing-slips' ) . '">' . __( 'Support Forum', 'woocommerce-pdf-invoices-packing-slips' ) . '</a>',
|
76 |
+
);
|
77 |
+
|
78 |
+
return array_merge( $links, $row_meta );
|
79 |
+
}
|
80 |
+
return (array) $links;
|
81 |
+
}
|
82 |
+
|
83 |
+
function check_auto_increment_increment() {
|
84 |
+
global $wpdb;
|
85 |
+
$row = $wpdb->get_row("SHOW VARIABLES LIKE 'auto_increment_increment'");
|
86 |
+
if ( !empty($row) && !empty($row->Value) && $row->Value != 1 ) {
|
87 |
+
$error = sprintf( __( "<strong>Warning!</strong> Your database has an AUTO_INCREMENT step size of %s, your invoice numbers may not be sequential. Enable the 'Calculate document numbers (slow)' setting in the Status tab to use an alternate method." , 'woocommerce-pdf-invoices-packing-slips' ), $row->Value );
|
88 |
+
printf( '<div class="error"><p>%s</p></div>', $error );
|
89 |
+
}
|
90 |
+
}
|
91 |
+
|
92 |
+
|
93 |
+
public function settings_page() {
|
94 |
+
$settings_tabs = apply_filters( 'wpo_wcpdf_settings_tabs', array (
|
95 |
+
'general' => __('General', 'woocommerce-pdf-invoices-packing-slips' ),
|
96 |
+
'documents' => __('Documents', 'woocommerce-pdf-invoices-packing-slips' ),
|
97 |
+
)
|
98 |
+
);
|
99 |
+
|
100 |
+
// add status tab last in row
|
101 |
+
$settings_tabs['debug'] = __('Status', 'woocommerce-pdf-invoices-packing-slips' );
|
102 |
+
|
103 |
+
$active_tab = isset( $_GET[ 'tab' ] ) ? sanitize_text_field( $_GET[ 'tab' ] ) : 'general';
|
104 |
+
$active_section = isset( $_GET[ 'section' ] ) ? sanitize_text_field( $_GET[ 'section' ] ) : '';
|
105 |
+
|
106 |
+
include('views/wcpdf-settings-page.php');
|
107 |
+
}
|
108 |
+
|
109 |
+
public function add_settings_fields( $settings_fields, $page, $option_group, $option_name ) {
|
110 |
+
foreach ( $settings_fields as $settings_field ) {
|
111 |
+
if (!isset($settings_field['callback'])) {
|
112 |
+
continue;
|
113 |
+
} elseif ( is_callable( array( $this->callbacks, $settings_field['callback'] ) ) ) {
|
114 |
+
$callback = array( $this->callbacks, $settings_field['callback'] );
|
115 |
+
} elseif ( is_callable( $settings_field['callback'] ) ) {
|
116 |
+
$callback = $settings_field['callback'];
|
117 |
+
} else {
|
118 |
+
continue;
|
119 |
+
}
|
120 |
+
|
121 |
+
if ( $settings_field['type'] == 'section' ) {
|
122 |
+
add_settings_section(
|
123 |
+
$settings_field['id'],
|
124 |
+
$settings_field['title'],
|
125 |
+
$callback,
|
126 |
+
$page
|
127 |
+
);
|
128 |
+
} else {
|
129 |
+
add_settings_field(
|
130 |
+
$settings_field['id'],
|
131 |
+
$settings_field['title'],
|
132 |
+
$callback,
|
133 |
+
$page,
|
134 |
+
$settings_field['section'],
|
135 |
+
$settings_field['args']
|
136 |
+
);
|
137 |
+
// register option separately for singular options
|
138 |
+
if (is_string($settings_field['callback']) && $settings_field['callback'] == 'singular_text_element') {
|
139 |
+
register_setting( $option_group, $settings_field['args']['option_name'], array( $this->callbacks, 'validate' ) );
|
140 |
+
}
|
141 |
+
}
|
142 |
+
}
|
143 |
+
// $page, $option_group & $option_name are all the same...
|
144 |
+
register_setting( $option_group, $option_name, array( $this->callbacks, 'validate' ) );
|
145 |
+
add_filter( 'option_page_capability_'.$page, array( $this, 'settings_capabilities' ) );
|
146 |
+
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* Set capability for settings page
|
151 |
+
*/
|
152 |
+
public function settings_capabilities() {
|
153 |
+
return 'manage_woocommerce';
|
154 |
+
}
|
155 |
+
|
156 |
+
public function get_common_document_settings() {
|
157 |
+
$common_settings = array(
|
158 |
+
'paper_size' => isset( $this->general_settings['paper_size'] ) ? $this->general_settings['paper_size'] : '',
|
159 |
+
'font_subsetting' => isset( $this->general_settings['font_subsetting'] ) || ( defined("DOMPDF_ENABLE_FONTSUBSETTING") && DOMPDF_ENABLE_FONTSUBSETTING === true ) ? true : false,
|
160 |
+
'header_logo' => isset( $this->general_settings['header_logo'] ) ? $this->general_settings['header_logo'] : '',
|
161 |
+
'shop_name' => isset( $this->general_settings['shop_name'] ) ? $this->general_settings['shop_name'] : '',
|
162 |
+
'shop_address' => isset( $this->general_settings['shop_address'] ) ? $this->general_settings['shop_address'] : '',
|
163 |
+
'footer' => isset( $this->general_settings['footer'] ) ? $this->general_settings['footer'] : '',
|
164 |
+
'extra_1' => isset( $this->general_settings['extra_1'] ) ? $this->general_settings['extra_1'] : '',
|
165 |
+
'extra_2' => isset( $this->general_settings['extra_2'] ) ? $this->general_settings['extra_2'] : '',
|
166 |
+
'extra_3' => isset( $this->general_settings['extra_3'] ) ? $this->general_settings['extra_3'] : '',
|
167 |
+
);
|
168 |
+
return $common_settings;
|
169 |
+
}
|
170 |
+
|
171 |
+
public function get_document_settings( $document_type ) {
|
172 |
+
$documents = WPO_WCPDF()->documents->get_documents('all');
|
173 |
+
foreach ($documents as $document) {
|
174 |
+
if ( $document->get_type() == $document_type ) {
|
175 |
+
return $document->settings;
|
176 |
+
}
|
177 |
+
}
|
178 |
+
return false;
|
179 |
+
}
|
180 |
+
|
181 |
+
public function get_output_format() {
|
182 |
+
if ( isset( $this->debug_settings['html_output'] ) ) {
|
183 |
+
$output_format = 'html';
|
184 |
+
} else {
|
185 |
+
$output_format = 'pdf';
|
186 |
+
}
|
187 |
+
return $output_format;
|
188 |
+
}
|
189 |
+
|
190 |
+
public function get_output_mode() {
|
191 |
+
if ( isset( WPO_WCPDF()->settings->general_settings['download_display'] ) ) {
|
192 |
+
switch ( WPO_WCPDF()->settings->general_settings['download_display'] ) {
|
193 |
+
case 'display':
|
194 |
+
$output_mode = 'inline';
|
195 |
+
break;
|
196 |
+
case 'download':
|
197 |
+
default:
|
198 |
+
$output_mode = 'download';
|
199 |
+
break;
|
200 |
+
}
|
201 |
+
} else {
|
202 |
+
$output_mode = 'download';
|
203 |
+
}
|
204 |
+
return $output_mode;
|
205 |
+
}
|
206 |
+
|
207 |
+
public function get_template_path( $document_type = NULL ) {
|
208 |
+
$template_path = isset( $this->general_settings['template_path'] )?$this->general_settings['template_path']:'';
|
209 |
+
// forward slash for consistency
|
210 |
+
$template_path = str_replace('\\','/', $template_path);
|
211 |
+
|
212 |
+
// add base path, checking if it's not already there
|
213 |
+
// alternative setups like Bedrock have WP_CONTENT_DIR & ABSPATH separated
|
214 |
+
if ( defined('WP_CONTENT_DIR') && strpos( WP_CONTENT_DIR, ABSPATH ) !== false ) {
|
215 |
+
$forwardslash_basepath = str_replace('\\','/', ABSPATH);
|
216 |
+
} else {
|
217 |
+
// bedrock e.a
|
218 |
+
$forwardslash_basepath = str_replace('\\','/', WP_CONTENT_DIR);
|
219 |
+
}
|
220 |
+
|
221 |
+
if ( strpos( $template_path, $forwardslash_basepath ) === false ) {
|
222 |
+
$template_path = $forwardslash_basepath . $template_path;
|
223 |
+
}
|
224 |
+
|
225 |
+
return $template_path;
|
226 |
+
}
|
227 |
+
|
228 |
+
public function set_number_store() {
|
229 |
+
check_ajax_referer( "wpo_wcpdf_next_{$_POST['store']}", 'security' );
|
230 |
+
$number = isset( $_POST['number'] ) ? (int) $_POST['number'] : 0;
|
231 |
+
$number_store_method = $this->get_sequential_number_store_method();
|
232 |
+
$number_store = new Sequential_Number_Store( $_POST['store'], $number_store_method );
|
233 |
+
$number_store->set_next( $number );
|
234 |
+
echo "next number ({$_POST['store']}) set to {$number}";
|
235 |
+
die();
|
236 |
+
}
|
237 |
+
|
238 |
+
public function get_sequential_number_store_method() {
|
239 |
+
global $wpdb;
|
240 |
+
$method = isset( $this->debug_settings['calculate_document_numbers'] ) ? 'calculate' : 'auto_increment';
|
241 |
+
|
242 |
+
// safety first - always use calculate when auto_increment_increment is not 1
|
243 |
+
$row = $wpdb->get_row("SHOW VARIABLES LIKE 'auto_increment_increment'");
|
244 |
+
if ( !empty($row) && !empty($row->Value) && $row->Value != 1 ) {
|
245 |
+
$method = 'calculate';
|
246 |
+
}
|
247 |
+
|
248 |
+
return $method;
|
249 |
+
}
|
250 |
+
|
251 |
+
}
|
252 |
+
|
253 |
+
endif; // class_exists
|
254 |
+
|
255 |
return new Settings();
|
includes/class-wcpdf-setup-wizard.php
CHANGED
@@ -1,247 +1,247 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPO\WC\PDF_Invoices;
|
3 |
-
|
4 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
5 |
-
exit; // Exit if accessed directly
|
6 |
-
}
|
7 |
-
|
8 |
-
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Setup_Wizard' ) ) :
|
9 |
-
|
10 |
-
class Setup_Wizard {
|
11 |
-
|
12 |
-
/** @var string Currenct Step */
|
13 |
-
private $step = '';
|
14 |
-
|
15 |
-
/** @var array Steps for the setup wizard */
|
16 |
-
private $steps = array();
|
17 |
-
|
18 |
-
public function __construct() {
|
19 |
-
if ( current_user_can( 'manage_woocommerce' ) ) {
|
20 |
-
add_action( 'admin_menu', array( $this, 'admin_menus' ) );
|
21 |
-
add_action( 'admin_init', array( $this, 'setup_wizard' ) );
|
22 |
-
}
|
23 |
-
|
24 |
-
}
|
25 |
-
|
26 |
-
/**
|
27 |
-
* Add admin menus/screens.
|
28 |
-
*/
|
29 |
-
public function admin_menus() {
|
30 |
-
add_dashboard_page( '', '', 'manage_options', 'wpo-wcpdf-setup', '' );
|
31 |
-
}
|
32 |
-
|
33 |
-
/**
|
34 |
-
* Show the setup wizard.
|
35 |
-
*/
|
36 |
-
public function setup_wizard() {
|
37 |
-
if ( empty( $_GET['page'] ) || 'wpo-wcpdf-setup' !== $_GET['page'] ) {
|
38 |
-
return;
|
39 |
-
}
|
40 |
-
$this->steps = array(
|
41 |
-
'shop-name' => array(
|
42 |
-
'name' => __( 'Shop Name', 'woocommerce-pdf-invoices-packing-slips' ),
|
43 |
-
'view' => WPO_WCPDF()->plugin_path() . '/includes/views/setup-wizard/shop-name.php',
|
44 |
-
),
|
45 |
-
'logo' => array(
|
46 |
-
'name' => __( 'Your logo', 'woocommerce-pdf-invoices-packing-slips' ),
|
47 |
-
'view' => WPO_WCPDF()->plugin_path() . '/includes/views/setup-wizard/logo.php',
|
48 |
-
),
|
49 |
-
'attach-to' => array(
|
50 |
-
'name' => __( 'Attachments', 'woocommerce-pdf-invoices-packing-slips' ),
|
51 |
-
'view' => WPO_WCPDF()->plugin_path() . '/includes/views/setup-wizard/attach-to.php',
|
52 |
-
),
|
53 |
-
'display-options' => array(
|
54 |
-
'name' => __( 'Display options', 'woocommerce-pdf-invoices-packing-slips' ),
|
55 |
-
'view' => WPO_WCPDF()->plugin_path() . '/includes/views/setup-wizard/display-options.php',
|
56 |
-
),
|
57 |
-
'paper-format' => array(
|
58 |
-
'name' => __( 'Paper format', 'woocommerce-pdf-invoices-packing-slips' ),
|
59 |
-
'view' => WPO_WCPDF()->plugin_path() . '/includes/views/setup-wizard/paper-format.php',
|
60 |
-
),
|
61 |
-
'good-to-go' => array(
|
62 |
-
'name' => __( 'Ready!', 'woocommerce-pdf-invoices-packing-slips' ),
|
63 |
-
'view' => WPO_WCPDF()->plugin_path() . '/includes/views/setup-wizard/good-to-go.php',
|
64 |
-
),
|
65 |
-
);
|
66 |
-
$this->step = isset( $_GET['step'] ) ? sanitize_key( $_GET['step'] ) : current( array_keys( $this->steps ) );
|
67 |
-
|
68 |
-
wp_enqueue_style(
|
69 |
-
'wpo-wcpdf-setup',
|
70 |
-
WPO_WCPDF()->plugin_url() . '/assets/css/setup-wizard.css',
|
71 |
-
array( 'dashicons', 'install' ),
|
72 |
-
WPO_WCPDF_VERSION
|
73 |
-
);
|
74 |
-
wp_register_script(
|
75 |
-
'wpo-wcpdf-media-upload',
|
76 |
-
WPO_WCPDF()->plugin_url() . '/assets/js/media-upload.js',
|
77 |
-
array( 'jquery', 'media-editor', 'mce-view' ),
|
78 |
-
WPO_WCPDF_VERSION
|
79 |
-
);
|
80 |
-
wp_register_script(
|
81 |
-
'wpo-wcpdf-setup',
|
82 |
-
WPO_WCPDF()->plugin_url() . '/assets/js/setup-wizard.js',
|
83 |
-
array( 'jquery', 'wpo-wcpdf-media-upload' ),
|
84 |
-
WPO_WCPDF_VERSION
|
85 |
-
);
|
86 |
-
wp_enqueue_media();
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
$step_keys = array_keys($this->steps);
|
91 |
-
if ( end( $step_keys ) === $this->step ) {
|
92 |
-
wp_register_script(
|
93 |
-
'wpo-wcpdf-setup-confetti',
|
94 |
-
WPO_WCPDF()->plugin_url() . '/assets/js/confetti.js',
|
95 |
-
array( 'jquery' ),
|
96 |
-
WPO_WCPDF_VERSION
|
97 |
-
);
|
98 |
-
}
|
99 |
-
|
100 |
-
if ( ! empty( $_POST['save_step'] ) ) {
|
101 |
-
$this->save_step();
|
102 |
-
// echo '<pre>';var_dump($_POST);echo '</pre>';die();
|
103 |
-
// call_user_func( $this->steps[ $this->step ]['handler'], $this );
|
104 |
-
}
|
105 |
-
|
106 |
-
ob_start();
|
107 |
-
$this->setup_wizard_header();
|
108 |
-
$this->setup_wizard_steps();
|
109 |
-
$this->setup_wizard_content();
|
110 |
-
$this->setup_wizard_footer();
|
111 |
-
exit;
|
112 |
-
}
|
113 |
-
|
114 |
-
/**
|
115 |
-
* Setup Wizard Header.
|
116 |
-
*/
|
117 |
-
public function setup_wizard_header() {
|
118 |
-
?>
|
119 |
-
<!DOCTYPE html>
|
120 |
-
<html <?php language_attributes(); ?> class="wpo-wizzard">
|
121 |
-
<head>
|
122 |
-
<meta name="viewport" content="width=device-width" />
|
123 |
-
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
124 |
-
<title><?php esc_html_e( 'WooCommerce PDF Invoices & Packing Slips › Setup Wizard', 'woocommerce-pdf-invoices-packing-slips' ); ?></title>
|
125 |
-
<?php wp_print_scripts( 'wpo-wcpdf-setup' ); ?>
|
126 |
-
<?php wp_print_scripts( 'wpo-wcpdf-setup-confetti' ); ?>
|
127 |
-
<?php do_action( 'admin_print_styles' ); ?>
|
128 |
-
<?php do_action( 'admin_head' ); ?>
|
129 |
-
</head>
|
130 |
-
<body class="wpo-wcpdf-setup wp-core-ui">
|
131 |
-
<?php if( $this->step == 'good-to-go' ) { echo "<div id='confetti'></div>"; } ?>
|
132 |
-
<form method="post">
|
133 |
-
<?php
|
134 |
-
}
|
135 |
-
|
136 |
-
/**
|
137 |
-
* Output the steps.
|
138 |
-
*/
|
139 |
-
public function setup_wizard_steps() {
|
140 |
-
$output_steps = $this->steps;
|
141 |
-
// array_shift( $output_steps );
|
142 |
-
?>
|
143 |
-
<div class="wpo-setup-card">
|
144 |
-
<h1 class="wpo-plugin-title">PDF Invoices & Packing Slips</h1>
|
145 |
-
<ol class="wpo-progress-bar">
|
146 |
-
<?php foreach ( $output_steps as $step_key => $step ) : ?>
|
147 |
-
<a href="<?php echo $this->get_step_link($step_key); ?>" ><li><div class="wpo-progress-marker <?php
|
148 |
-
if ( $step_key === $this->step ) {
|
149 |
-
echo 'active';
|
150 |
-
} elseif ( array_search( $this->step, array_keys( $this->steps ) ) > array_search( $step_key, array_keys( $this->steps ) ) ) {
|
151 |
-
echo 'completed';
|
152 |
-
}
|
153 |
-
?>"><?php //echo esc_html( $step['name'] ); ?></div></li></a>
|
154 |
-
<?php endforeach; ?>
|
155 |
-
</ol>
|
156 |
-
<?php
|
157 |
-
}
|
158 |
-
|
159 |
-
/**
|
160 |
-
* Output the content for the current step.
|
161 |
-
*/
|
162 |
-
public function setup_wizard_content() {
|
163 |
-
echo '<div class="wpo-setup-content">';
|
164 |
-
include( $this->steps[ $this->step ]['view'] );
|
165 |
-
echo '</div>';
|
166 |
-
}
|
167 |
-
|
168 |
-
/**
|
169 |
-
* Setup Wizard Footer.
|
170 |
-
*/
|
171 |
-
public function setup_wizard_footer() {
|
172 |
-
?>
|
173 |
-
<div class="wpo-setup-buttons">
|
174 |
-
<?php if ($step = $this->get_step(-1)): ?>
|
175 |
-
<a href="<?php echo $this->get_step_link($step); ?>" class="wpo-button-previous"><?php _e( 'Previous', 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
176 |
-
<?php endif ?>
|
177 |
-
<!-- <input type="submit" class="wpo-button-next" value="Next" /> -->
|
178 |
-
<?php if ($step = $this->get_step(1)): ?>
|
179 |
-
<?php wp_nonce_field( 'wpo-wcpdf-setup' ); ?>
|
180 |
-
<input type="submit" class="wpo-button-next" value="<?php esc_attr_e( 'Next', 'woocommerce-pdf-invoices-packing-slips' ); ?>" name="save_step" />
|
181 |
-
<a href="<?php echo $this->get_step_link($step); ?>" class="wpo-skip-step"><?php _e( 'Skip this step', 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
182 |
-
<?php else: ?>
|
183 |
-
<a href="<?php echo $this->get_step_link($step); ?>" class="wpo-button-next"><?php _e( 'Finish', 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
184 |
-
<?php endif ?>
|
185 |
-
</div>
|
186 |
-
</div>
|
187 |
-
</form>
|
188 |
-
<?php do_action( 'admin_footer' ); // for media uploader templates ?>
|
189 |
-
</body>
|
190 |
-
</html>
|
191 |
-
<?php
|
192 |
-
}
|
193 |
-
|
194 |
-
public function get_step_link( $step ) {
|
195 |
-
$step_keys = array_keys( $this->steps );
|
196 |
-
if ( end( $step_keys ) === $this->step && empty($step)) {
|
197 |
-
return admin_url();
|
198 |
-
}
|
199 |
-
return add_query_arg( 'step', $step );
|
200 |
-
}
|
201 |
-
|
202 |
-
|
203 |
-
public function get_step( $delta ) {
|
204 |
-
$step_keys = array_keys( $this->steps );
|
205 |
-
$current_step_pos = array_search( $this->step, $step_keys );
|
206 |
-
$new_step_pos = $current_step_pos + $delta;
|
207 |
-
if (isset($step_keys[$new_step_pos])) {
|
208 |
-
return $step_keys[$new_step_pos];
|
209 |
-
} else {
|
210 |
-
return false;
|
211 |
-
}
|
212 |
-
}
|
213 |
-
|
214 |
-
public function save_step() {
|
215 |
-
if ( isset( $this->steps[ $this->step ]['handler'] ) ) {
|
216 |
-
check_admin_referer( 'wpo-wcpdf-setup' );
|
217 |
-
// for doing more than just saving an option value
|
218 |
-
call_user_func( $this->steps[ $this->step ]['handler'] );
|
219 |
-
} else {
|
220 |
-
if (!empty($_POST['wcpdf_settings']) && is_array($_POST['wcpdf_settings'])) {
|
221 |
-
check_admin_referer( 'wpo-wcpdf-setup' );
|
222 |
-
foreach ($_POST['wcpdf_settings'] as $option => $settings) {
|
223 |
-
// sanitize posted settings
|
224 |
-
foreach ($settings as $key => $value) {
|
225 |
-
if (is_array($value)) {
|
226 |
-
$settings[$key] = array_map('sanitize_text_field', $value);
|
227 |
-
} else {
|
228 |
-
$settings[$key] = sanitize_text_field( $value );
|
229 |
-
}
|
230 |
-
}
|
231 |
-
$current_settings = get_option( $option, array() );
|
232 |
-
$new_settings = $settings + $current_settings;
|
233 |
-
// echo "<pre>".var_export($settings,true)."</pre>";
|
234 |
-
// echo "<pre>".var_export($new_settings,true)."</pre>";die();
|
235 |
-
update_option( $option, $new_settings );
|
236 |
-
}
|
237 |
-
}
|
238 |
-
}
|
239 |
-
|
240 |
-
wp_redirect( esc_url_raw( $this->get_step_link( $this->get_step(1) ) ) );
|
241 |
-
}
|
242 |
-
|
243 |
-
}
|
244 |
-
|
245 |
-
endif; // class_exists
|
246 |
-
|
247 |
return new Setup_Wizard();
|
1 |
+
<?php
|
2 |
+
namespace WPO\WC\PDF_Invoices;
|
3 |
+
|
4 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
5 |
+
exit; // Exit if accessed directly
|
6 |
+
}
|
7 |
+
|
8 |
+
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Setup_Wizard' ) ) :
|
9 |
+
|
10 |
+
class Setup_Wizard {
|
11 |
+
|
12 |
+
/** @var string Currenct Step */
|
13 |
+
private $step = '';
|
14 |
+
|
15 |
+
/** @var array Steps for the setup wizard */
|
16 |
+
private $steps = array();
|
17 |
+
|
18 |
+
public function __construct() {
|
19 |
+
if ( current_user_can( 'manage_woocommerce' ) ) {
|
20 |
+
add_action( 'admin_menu', array( $this, 'admin_menus' ) );
|
21 |
+
add_action( 'admin_init', array( $this, 'setup_wizard' ) );
|
22 |
+
}
|
23 |
+
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Add admin menus/screens.
|
28 |
+
*/
|
29 |
+
public function admin_menus() {
|
30 |
+
add_dashboard_page( '', '', 'manage_options', 'wpo-wcpdf-setup', '' );
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Show the setup wizard.
|
35 |
+
*/
|
36 |
+
public function setup_wizard() {
|
37 |
+
if ( empty( $_GET['page'] ) || 'wpo-wcpdf-setup' !== $_GET['page'] ) {
|
38 |
+
return;
|
39 |
+
}
|
40 |
+
$this->steps = array(
|
41 |
+
'shop-name' => array(
|
42 |
+
'name' => __( 'Shop Name', 'woocommerce-pdf-invoices-packing-slips' ),
|
43 |
+
'view' => WPO_WCPDF()->plugin_path() . '/includes/views/setup-wizard/shop-name.php',
|
44 |
+
),
|
45 |
+
'logo' => array(
|
46 |
+
'name' => __( 'Your logo', 'woocommerce-pdf-invoices-packing-slips' ),
|
47 |
+
'view' => WPO_WCPDF()->plugin_path() . '/includes/views/setup-wizard/logo.php',
|
48 |
+
),
|
49 |
+
'attach-to' => array(
|
50 |
+
'name' => __( 'Attachments', 'woocommerce-pdf-invoices-packing-slips' ),
|
51 |
+
'view' => WPO_WCPDF()->plugin_path() . '/includes/views/setup-wizard/attach-to.php',
|
52 |
+
),
|
53 |
+
'display-options' => array(
|
54 |
+
'name' => __( 'Display options', 'woocommerce-pdf-invoices-packing-slips' ),
|
55 |
+
'view' => WPO_WCPDF()->plugin_path() . '/includes/views/setup-wizard/display-options.php',
|
56 |
+
),
|
57 |
+
'paper-format' => array(
|
58 |
+
'name' => __( 'Paper format', 'woocommerce-pdf-invoices-packing-slips' ),
|
59 |
+
'view' => WPO_WCPDF()->plugin_path() . '/includes/views/setup-wizard/paper-format.php',
|
60 |
+
),
|
61 |
+
'good-to-go' => array(
|
62 |
+
'name' => __( 'Ready!', 'woocommerce-pdf-invoices-packing-slips' ),
|
63 |
+
'view' => WPO_WCPDF()->plugin_path() . '/includes/views/setup-wizard/good-to-go.php',
|
64 |
+
),
|
65 |
+
);
|
66 |
+
$this->step = isset( $_GET['step'] ) ? sanitize_key( $_GET['step'] ) : current( array_keys( $this->steps ) );
|
67 |
+
|
68 |
+
wp_enqueue_style(
|
69 |
+
'wpo-wcpdf-setup',
|
70 |
+
WPO_WCPDF()->plugin_url() . '/assets/css/setup-wizard.css',
|
71 |
+
array( 'dashicons', 'install' ),
|
72 |
+
WPO_WCPDF_VERSION
|
73 |
+
);
|
74 |
+
wp_register_script(
|
75 |
+
'wpo-wcpdf-media-upload',
|
76 |
+
WPO_WCPDF()->plugin_url() . '/assets/js/media-upload.js',
|
77 |
+
array( 'jquery', 'media-editor', 'mce-view' ),
|
78 |
+
WPO_WCPDF_VERSION
|
79 |
+
);
|
80 |
+
wp_register_script(
|
81 |
+
'wpo-wcpdf-setup',
|
82 |
+
WPO_WCPDF()->plugin_url() . '/assets/js/setup-wizard.js',
|
83 |
+
array( 'jquery', 'wpo-wcpdf-media-upload' ),
|
84 |
+
WPO_WCPDF_VERSION
|
85 |
+
);
|
86 |
+
wp_enqueue_media();
|
87 |
+
|
88 |
+
|
89 |
+
|
90 |
+
$step_keys = array_keys($this->steps);
|
91 |
+
if ( end( $step_keys ) === $this->step ) {
|
92 |
+
wp_register_script(
|
93 |
+
'wpo-wcpdf-setup-confetti',
|
94 |
+
WPO_WCPDF()->plugin_url() . '/assets/js/confetti.js',
|
95 |
+
array( 'jquery' ),
|
96 |
+
WPO_WCPDF_VERSION
|
97 |
+
);
|
98 |
+
}
|
99 |
+
|
100 |
+
if ( ! empty( $_POST['save_step'] ) ) {
|
101 |
+
$this->save_step();
|
102 |
+
// echo '<pre>';var_dump($_POST);echo '</pre>';die();
|
103 |
+
// call_user_func( $this->steps[ $this->step ]['handler'], $this );
|
104 |
+
}
|
105 |
+
|
106 |
+
ob_start();
|
107 |
+
$this->setup_wizard_header();
|
108 |
+
$this->setup_wizard_steps();
|
109 |
+
$this->setup_wizard_content();
|
110 |
+
$this->setup_wizard_footer();
|
111 |
+
exit;
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Setup Wizard Header.
|
116 |
+
*/
|
117 |
+
public function setup_wizard_header() {
|
118 |
+
?>
|
119 |
+
<!DOCTYPE html>
|
120 |
+
<html <?php language_attributes(); ?> class="wpo-wizzard">
|
121 |
+
<head>
|
122 |
+
<meta name="viewport" content="width=device-width" />
|
123 |
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
124 |
+
<title><?php esc_html_e( 'WooCommerce PDF Invoices & Packing Slips › Setup Wizard', 'woocommerce-pdf-invoices-packing-slips' ); ?></title>
|
125 |
+
<?php wp_print_scripts( 'wpo-wcpdf-setup' ); ?>
|
126 |
+
<?php wp_print_scripts( 'wpo-wcpdf-setup-confetti' ); ?>
|
127 |
+
<?php do_action( 'admin_print_styles' ); ?>
|
128 |
+
<?php do_action( 'admin_head' ); ?>
|
129 |
+
</head>
|
130 |
+
<body class="wpo-wcpdf-setup wp-core-ui">
|
131 |
+
<?php if( $this->step == 'good-to-go' ) { echo "<div id='confetti'></div>"; } ?>
|
132 |
+
<form method="post">
|
133 |
+
<?php
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Output the steps.
|
138 |
+
*/
|
139 |
+
public function setup_wizard_steps() {
|
140 |
+
$output_steps = $this->steps;
|
141 |
+
// array_shift( $output_steps );
|
142 |
+
?>
|
143 |
+
<div class="wpo-setup-card">
|
144 |
+
<h1 class="wpo-plugin-title">PDF Invoices & Packing Slips</h1>
|
145 |
+
<ol class="wpo-progress-bar">
|
146 |
+
<?php foreach ( $output_steps as $step_key => $step ) : ?>
|
147 |
+
<a href="<?php echo $this->get_step_link($step_key); ?>" ><li><div class="wpo-progress-marker <?php
|
148 |
+
if ( $step_key === $this->step ) {
|
149 |
+
echo 'active';
|
150 |
+
} elseif ( array_search( $this->step, array_keys( $this->steps ) ) > array_search( $step_key, array_keys( $this->steps ) ) ) {
|
151 |
+
echo 'completed';
|
152 |
+
}
|
153 |
+
?>"><?php //echo esc_html( $step['name'] ); ?></div></li></a>
|
154 |
+
<?php endforeach; ?>
|
155 |
+
</ol>
|
156 |
+
<?php
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Output the content for the current step.
|
161 |
+
*/
|
162 |
+
public function setup_wizard_content() {
|
163 |
+
echo '<div class="wpo-setup-content">';
|
164 |
+
include( $this->steps[ $this->step ]['view'] );
|
165 |
+
echo '</div>';
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Setup Wizard Footer.
|
170 |
+
*/
|
171 |
+
public function setup_wizard_footer() {
|
172 |
+
?>
|
173 |
+
<div class="wpo-setup-buttons">
|
174 |
+
<?php if ($step = $this->get_step(-1)): ?>
|
175 |
+
<a href="<?php echo $this->get_step_link($step); ?>" class="wpo-button-previous"><?php _e( 'Previous', 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
176 |
+
<?php endif ?>
|
177 |
+
<!-- <input type="submit" class="wpo-button-next" value="Next" /> -->
|
178 |
+
<?php if ($step = $this->get_step(1)): ?>
|
179 |
+
<?php wp_nonce_field( 'wpo-wcpdf-setup' ); ?>
|
180 |
+
<input type="submit" class="wpo-button-next" value="<?php esc_attr_e( 'Next', 'woocommerce-pdf-invoices-packing-slips' ); ?>" name="save_step" />
|
181 |
+
<a href="<?php echo $this->get_step_link($step); ?>" class="wpo-skip-step"><?php _e( 'Skip this step', 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
182 |
+
<?php else: ?>
|
183 |
+
<a href="<?php echo $this->get_step_link($step); ?>" class="wpo-button-next"><?php _e( 'Finish', 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
184 |
+
<?php endif ?>
|
185 |
+
</div>
|
186 |
+
</div>
|
187 |
+
</form>
|
188 |
+
<?php do_action( 'admin_footer' ); // for media uploader templates ?>
|
189 |
+
</body>
|
190 |
+
</html>
|
191 |
+
<?php
|
192 |
+
}
|
193 |
+
|
194 |
+
public function get_step_link( $step ) {
|
195 |
+
$step_keys = array_keys( $this->steps );
|
196 |
+
if ( end( $step_keys ) === $this->step && empty($step)) {
|
197 |
+
return admin_url();
|
198 |
+
}
|
199 |
+
return add_query_arg( 'step', $step );
|
200 |
+
}
|
201 |
+
|
202 |
+
|
203 |
+
public function get_step( $delta ) {
|
204 |
+
$step_keys = array_keys( $this->steps );
|
205 |
+
$current_step_pos = array_search( $this->step, $step_keys );
|
206 |
+
$new_step_pos = $current_step_pos + $delta;
|
207 |
+
if (isset($step_keys[$new_step_pos])) {
|
208 |
+
return $step_keys[$new_step_pos];
|
209 |
+
} else {
|
210 |
+
return false;
|
211 |
+
}
|
212 |
+
}
|
213 |
+
|
214 |
+
public function save_step() {
|
215 |
+
if ( isset( $this->steps[ $this->step ]['handler'] ) ) {
|
216 |
+
check_admin_referer( 'wpo-wcpdf-setup' );
|
217 |
+
// for doing more than just saving an option value
|
218 |
+
call_user_func( $this->steps[ $this->step ]['handler'] );
|
219 |
+
} else {
|
220 |
+
if (!empty($_POST['wcpdf_settings']) && is_array($_POST['wcpdf_settings'])) {
|
221 |
+
check_admin_referer( 'wpo-wcpdf-setup' );
|
222 |
+
foreach ($_POST['wcpdf_settings'] as $option => $settings) {
|
223 |
+
// sanitize posted settings
|
224 |
+
foreach ($settings as $key => $value) {
|
225 |
+
if (is_array($value)) {
|
226 |
+
$settings[$key] = array_map('sanitize_text_field', $value);
|
227 |
+
} else {
|
228 |
+
$settings[$key] = sanitize_text_field( $value );
|
229 |
+
}
|
230 |
+
}
|
231 |
+
$current_settings = get_option( $option, array() );
|
232 |
+
$new_settings = $settings + $current_settings;
|
233 |
+
// echo "<pre>".var_export($settings,true)."</pre>";
|
234 |
+
// echo "<pre>".var_export($new_settings,true)."</pre>";die();
|
235 |
+
update_option( $option, $new_settings );
|
236 |
+
}
|
237 |
+
}
|
238 |
+
}
|
239 |
+
|
240 |
+
wp_redirect( esc_url_raw( $this->get_step_link( $this->get_step(1) ) ) );
|
241 |
+
}
|
242 |
+
|
243 |
+
}
|
244 |
+
|
245 |
+
endif; // class_exists
|
246 |
+
|
247 |
return new Setup_Wizard();
|
includes/compatibility/class-wcpdf-compatibility-third-party-plugins.php
CHANGED
@@ -1,162 +1,162 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPO\WC\PDF_Invoices\Compatibility;
|
3 |
-
|
4 |
-
use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
|
5 |
-
use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
|
6 |
-
use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
|
7 |
-
use WPO\WC\PDF_Invoices\Compatibility\WC_DateTime;
|
8 |
-
|
9 |
-
defined( 'ABSPATH' ) or exit;
|
10 |
-
|
11 |
-
if ( ! class_exists( '\\WPO\\WC\\PDF_Invoices\\Compatibility\\Third_Party_Plugins' ) ) :
|
12 |
-
|
13 |
-
/**
|
14 |
-
* Third party plugin compatibility class.
|
15 |
-
*
|
16 |
-
* @since 2.0
|
17 |
-
*/
|
18 |
-
class Third_Party_Plugins {
|
19 |
-
function __construct() {
|
20 |
-
// WooCommerce Subscriptions compatibility
|
21 |
-
if ( class_exists('WC_Subscriptions') ) {
|
22 |
-
if ( version_compare( \WC_Subscriptions::$version, '2.0', '<' ) ) {
|
23 |
-
add_action( 'woocommerce_subscriptions_renewal_order_created', array( $this, 'woocommerce_subscriptions_renewal_order_created' ), 10, 4 );
|
24 |
-
} else {
|
25 |
-
add_action( 'wcs_renewal_order_created', array( $this, 'wcs_renewal_order_created' ), 10, 2 );
|
26 |
-
}
|
27 |
-
}
|
28 |
-
|
29 |
-
// WooCommerce Product Bundles compatibility (add row classes)
|
30 |
-
if ( class_exists('WC_Bundles') ) {
|
31 |
-
add_filter( 'wpo_wcpdf_item_row_class', array( $this, 'add_product_bundles_classes' ), 10, 4 );
|
32 |
-
}
|
33 |
-
|
34 |
-
// WooCommerce Chained Products compatibility (add row classes)
|
35 |
-
if ( class_exists('SA_WC_Chained_Products') ) {
|
36 |
-
add_filter( 'wpo_wcpdf_item_row_class', array( $this, 'add_chained_product_class' ), 10, 4 );
|
37 |
-
}
|
38 |
-
|
39 |
-
// WooCommerce Order Status & Actions Manager emails compatibility
|
40 |
-
if (class_exists('WC_Custom_Status')) {
|
41 |
-
add_filter( 'wpo_wcpdf_wc_emails', array( $this, 'wc_order_status_actions_emails' ), 10, 1 );
|
42 |
-
}
|
43 |
-
|
44 |
-
}
|
45 |
-
|
46 |
-
/**
|
47 |
-
* Reset invoice data for WooCommerce subscription renewal orders
|
48 |
-
* https://wordpress.org/support/topic/subscription-renewal-duplicate-invoice-number?replies=6#post-6138110
|
49 |
-
*/
|
50 |
-
public function woocommerce_subscriptions_renewal_order_created ( $renewal_order, $original_order, $product_id, $new_order_role ) {
|
51 |
-
$this->reset_invoice_data( $renewal_order );
|
52 |
-
return $renewal_order;
|
53 |
-
}
|
54 |
-
|
55 |
-
public function wcs_renewal_order_created ( $renewal_order, $subscription ) {
|
56 |
-
$this->reset_invoice_data( $renewal_order );
|
57 |
-
return $renewal_order;
|
58 |
-
}
|
59 |
-
|
60 |
-
public function reset_invoice_data ( $order ) {
|
61 |
-
if ( ! is_object( $order ) ) {
|
62 |
-
$order = wc_get_order( $order );
|
63 |
-
}
|
64 |
-
// delete invoice number, invoice date & invoice exists meta
|
65 |
-
WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_number' );
|
66 |
-
WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_number_data' );
|
67 |
-
WCX_Order::delete_meta_data( $order, '_wcpdf_formatted_invoice_number' );
|
68 |
-
WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_date' );
|
69 |
-
WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_exists' );
|
70 |
-
}
|
71 |
-
|
72 |
-
/**
|
73 |
-
* WooCommerce Product Bundles
|
74 |
-
* @param string $classes CSS classes for item row (tr)
|
75 |
-
* @param string $document_type PDF Document type
|
76 |
-
* @param object $order WC_Order order
|
77 |
-
* @param int $item_id WooCommerce Item ID
|
78 |
-
*/
|
79 |
-
public function add_product_bundles_classes ( $classes, $document_type, $order, $item_id = '' ) {
|
80 |
-
if ( empty($item_id) ) {
|
81 |
-
// get item id from classes (backwards compatibility fix)
|
82 |
-
$class_array = explode(' ', $classes);
|
83 |
-
foreach ($class_array as $class) {
|
84 |
-
if (is_numeric($class)) {
|
85 |
-
$item_id = $class;
|
86 |
-
break;
|
87 |
-
}
|
88 |
-
}
|
89 |
-
|
90 |
-
// if still empty, we lost the item id somewhere :(
|
91 |
-
if (empty($item_id)) {
|
92 |
-
return $classes;
|
93 |
-
}
|
94 |
-
}
|
95 |
-
|
96 |
-
if ( $bundled_by = WCX_Order::get_item_meta( $order, $item_id, '_bundled_by', true ) ) {
|
97 |
-
$classes = $classes . ' bundled-item';
|
98 |
-
|
99 |
-
// check bundled item visibility
|
100 |
-
if ( $hidden = WCX_Order::get_item_meta( $order, $item_id, '_bundled_item_hidden', true ) ) {
|
101 |
-
$classes = $classes . ' hidden';
|
102 |
-
}
|
103 |
-
|
104 |
-
return $classes;
|
105 |
-
} elseif ( $bundled_items = WCX_Order::get_item_meta( $order, $item_id, '_bundled_items', true ) ) {
|
106 |
-
return $classes . ' product-bundle';
|
107 |
-
}
|
108 |
-
|
109 |
-
return $classes;
|
110 |
-
}
|
111 |
-
|
112 |
-
/**
|
113 |
-
* WooCommerce Chanined Products
|
114 |
-
* @param string $classes CSS classes for item row (tr)
|
115 |
-
* @param string $document_type PDF Document type
|
116 |
-
* @param object $order WC_Order order
|
117 |
-
* @param int $item_id WooCommerce Item ID
|
118 |
-
*/
|
119 |
-
public function add_chained_product_class ( $classes, $document_type, $order, $item_id = '' ) {
|
120 |
-
if ( empty($item_id) ) {
|
121 |
-
// get item id from classes (backwards compatibility fix)
|
122 |
-
$class_array = explode(' ', $classes);
|
123 |
-
foreach ($class_array as $class) {
|
124 |
-
if (is_numeric($class)) {
|
125 |
-
$item_id = $class;
|
126 |
-
break;
|
127 |
-
}
|
128 |
-
}
|
129 |
-
|
130 |
-
// if still empty, we lost the item id somewhere :(
|
131 |
-
if (empty($item_id)) {
|
132 |
-
return $classes;
|
133 |
-
}
|
134 |
-
}
|
135 |
-
|
136 |
-
if ( $chained_product_of = WCX_Order::get_item_meta( $order, $item_id, '_chained_product_of', true ) ) {
|
137 |
-
return $classes . ' chained-product';
|
138 |
-
}
|
139 |
-
|
140 |
-
return $classes;
|
141 |
-
}
|
142 |
-
|
143 |
-
/**
|
144 |
-
* WooCommerce Order Status & Actions Manager emails compatibility
|
145 |
-
*/
|
146 |
-
public function wc_order_status_actions_emails ( $emails ) {
|
147 |
-
// get list of custom statuses from WooCommerce Custom Order Status & Actions
|
148 |
-
// status slug => status name
|
149 |
-
$custom_statuses = \WC_Custom_Status::get_status_list_names();
|
150 |
-
// append _email to slug (=email_id) and add to emails list
|
151 |
-
foreach ($custom_statuses as $status_slug => $status_name) {
|
152 |
-
$emails[$status_slug.'_email'] = $status_name;
|
153 |
-
}
|
154 |
-
return $emails;
|
155 |
-
}
|
156 |
-
|
157 |
-
}
|
158 |
-
|
159 |
-
|
160 |
-
endif; // Class exists check
|
161 |
-
|
162 |
-
return new Third_Party_Plugins();
|
1 |
+
<?php
|
2 |
+
namespace WPO\WC\PDF_Invoices\Compatibility;
|
3 |
+
|
4 |
+
use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
|
5 |
+
use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
|
6 |
+
use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
|
7 |
+
use WPO\WC\PDF_Invoices\Compatibility\WC_DateTime;
|
8 |
+
|
9 |
+
defined( 'ABSPATH' ) or exit;
|
10 |
+
|
11 |
+
if ( ! class_exists( '\\WPO\\WC\\PDF_Invoices\\Compatibility\\Third_Party_Plugins' ) ) :
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Third party plugin compatibility class.
|
15 |
+
*
|
16 |
+
* @since 2.0
|
17 |
+
*/
|
18 |
+
class Third_Party_Plugins {
|
19 |
+
function __construct() {
|
20 |
+
// WooCommerce Subscriptions compatibility
|
21 |
+
if ( class_exists('WC_Subscriptions') ) {
|
22 |
+
if ( version_compare( \WC_Subscriptions::$version, '2.0', '<' ) ) {
|
23 |
+
add_action( 'woocommerce_subscriptions_renewal_order_created', array( $this, 'woocommerce_subscriptions_renewal_order_created' ), 10, 4 );
|
24 |
+
} else {
|
25 |
+
add_action( 'wcs_renewal_order_created', array( $this, 'wcs_renewal_order_created' ), 10, 2 );
|
26 |
+
}
|
27 |
+
}
|
28 |
+
|
29 |
+
// WooCommerce Product Bundles compatibility (add row classes)
|
30 |
+
if ( class_exists('WC_Bundles') ) {
|
31 |
+
add_filter( 'wpo_wcpdf_item_row_class', array( $this, 'add_product_bundles_classes' ), 10, 4 );
|
32 |
+
}
|
33 |
+
|
34 |
+
// WooCommerce Chained Products compatibility (add row classes)
|
35 |
+
if ( class_exists('SA_WC_Chained_Products') ) {
|
36 |
+
add_filter( 'wpo_wcpdf_item_row_class', array( $this, 'add_chained_product_class' ), 10, 4 );
|
37 |
+
}
|
38 |
+
|
39 |
+
// WooCommerce Order Status & Actions Manager emails compatibility
|
40 |
+
if (class_exists('WC_Custom_Status')) {
|
41 |
+
add_filter( 'wpo_wcpdf_wc_emails', array( $this, 'wc_order_status_actions_emails' ), 10, 1 );
|
42 |
+
}
|
43 |
+
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Reset invoice data for WooCommerce subscription renewal orders
|
48 |
+
* https://wordpress.org/support/topic/subscription-renewal-duplicate-invoice-number?replies=6#post-6138110
|
49 |
+
*/
|
50 |
+
public function woocommerce_subscriptions_renewal_order_created ( $renewal_order, $original_order, $product_id, $new_order_role ) {
|
51 |
+
$this->reset_invoice_data( $renewal_order );
|
52 |
+
return $renewal_order;
|
53 |
+
}
|
54 |
+
|
55 |
+
public function wcs_renewal_order_created ( $renewal_order, $subscription ) {
|
56 |
+
$this->reset_invoice_data( $renewal_order );
|
57 |
+
return $renewal_order;
|
58 |
+
}
|
59 |
+
|
60 |
+
public function reset_invoice_data ( $order ) {
|
61 |
+
if ( ! is_object( $order ) ) {
|
62 |
+
$order = wc_get_order( $order );
|
63 |
+
}
|
64 |
+
// delete invoice number, invoice date & invoice exists meta
|
65 |
+
WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_number' );
|
66 |
+
WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_number_data' );
|
67 |
+
WCX_Order::delete_meta_data( $order, '_wcpdf_formatted_invoice_number' );
|
68 |
+
WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_date' );
|
69 |
+
WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_exists' );
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* WooCommerce Product Bundles
|
74 |
+
* @param string $classes CSS classes for item row (tr)
|
75 |
+
* @param string $document_type PDF Document type
|
76 |
+
* @param object $order WC_Order order
|
77 |
+
* @param int $item_id WooCommerce Item ID
|
78 |
+
*/
|
79 |
+
public function add_product_bundles_classes ( $classes, $document_type, $order, $item_id = '' ) {
|
80 |
+
if ( empty($item_id) ) {
|
81 |
+
// get item id from classes (backwards compatibility fix)
|
82 |
+
$class_array = explode(' ', $classes);
|
83 |
+
foreach ($class_array as $class) {
|
84 |
+
if (is_numeric($class)) {
|
85 |
+
$item_id = $class;
|
86 |
+
break;
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
// if still empty, we lost the item id somewhere :(
|
91 |
+
if (empty($item_id)) {
|
92 |
+
return $classes;
|
93 |
+
}
|
94 |
+
}
|
95 |
+
|
96 |
+
if ( $bundled_by = WCX_Order::get_item_meta( $order, $item_id, '_bundled_by', true ) ) {
|
97 |
+
$classes = $classes . ' bundled-item';
|
98 |
+
|
99 |
+
// check bundled item visibility
|
100 |
+
if ( $hidden = WCX_Order::get_item_meta( $order, $item_id, '_bundled_item_hidden', true ) ) {
|
101 |
+
$classes = $classes . ' hidden';
|
102 |
+
}
|
103 |
+
|
104 |
+
return $classes;
|
105 |
+
} elseif ( $bundled_items = WCX_Order::get_item_meta( $order, $item_id, '_bundled_items', true ) ) {
|
106 |
+
return $classes . ' product-bundle';
|
107 |
+
}
|
108 |
+
|
109 |
+
return $classes;
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* WooCommerce Chanined Products
|
114 |
+
* @param string $classes CSS classes for item row (tr)
|
115 |
+
* @param string $document_type PDF Document type
|
116 |
+
* @param object $order WC_Order order
|
117 |
+
* @param int $item_id WooCommerce Item ID
|
118 |
+
*/
|
119 |
+
public function add_chained_product_class ( $classes, $document_type, $order, $item_id = '' ) {
|
120 |
+
if ( empty($item_id) ) {
|
121 |
+
// get item id from classes (backwards compatibility fix)
|
122 |
+
$class_array = explode(' ', $classes);
|
123 |
+
foreach ($class_array as $class) {
|
124 |
+
if (is_numeric($class)) {
|
125 |
+
$item_id = $class;
|
126 |
+
break;
|
127 |
+
}
|
128 |
+
}
|
129 |
+
|
130 |
+
// if still empty, we lost the item id somewhere :(
|
131 |
+
if (empty($item_id)) {
|
132 |
+
return $classes;
|
133 |
+
}
|
134 |
+
}
|
135 |
+
|
136 |
+
if ( $chained_product_of = WCX_Order::get_item_meta( $order, $item_id, '_chained_product_of', true ) ) {
|
137 |
+
return $classes . ' chained-product';
|
138 |
+
}
|
139 |
+
|
140 |
+
return $classes;
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* WooCommerce Order Status & Actions Manager emails compatibility
|
145 |
+
*/
|
146 |
+
public function wc_order_status_actions_emails ( $emails ) {
|
147 |
+
// get list of custom statuses from WooCommerce Custom Order Status & Actions
|
148 |
+
// status slug => status name
|
149 |
+
$custom_statuses = \WC_Custom_Status::get_status_list_names();
|
150 |
+
// append _email to slug (=email_id) and add to emails list
|
151 |
+
foreach ($custom_statuses as $status_slug => $status_name) {
|
152 |
+
$emails[$status_slug.'_email'] = $status_name;
|
153 |
+
}
|
154 |
+
return $emails;
|
155 |
+
}
|
156 |
+
|
157 |
+
}
|
158 |
+
|
159 |
+
|
160 |
+
endif; // Class exists check
|
161 |
+
|
162 |
+
return new Third_Party_Plugins();
|
includes/documents/abstract-wcpdf-order-document-methods.php
CHANGED
@@ -1,1030 +1,1030 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPO\WC\PDF_Invoices\Documents;
|
3 |
-
|
4 |
-
use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
|
5 |
-
use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
|
6 |
-
use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
|
7 |
-
|
8 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
9 |
-
exit; // Exit if accessed directly
|
10 |
-
}
|
11 |
-
|
12 |
-
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Documents\\Order_Document_Methods' ) ) :
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Abstract Order Methods
|
16 |
-
*
|
17 |
-
* Collection of methods to be used on orders within a Document
|
18 |
-
* Created as abstract rather than traits to support PHP versions older than 5.4
|
19 |
-
*
|
20 |
-
* @class \WPO\WC\PDF_Invoices\Documents\Order_Document_Methods
|
21 |
-
* @version 2.0
|
22 |
-
* @category Class
|
23 |
-
* @author Ewout Fernhout
|
24 |
-
*/
|
25 |
-
|
26 |
-
abstract class Order_Document_Methods extends Order_Document {
|
27 |
-
public function is_refund( $order ) {
|
28 |
-
if ( method_exists( $order, 'get_type') ) { // WC 3.0+
|
29 |
-
$is_refund = $order->get_type() == 'shop_order_refund';
|
30 |
-
} else {
|
31 |
-
$is_refund = get_post_type( WCX_Order::get_id( $order ) ) == 'shop_order_refund';
|
32 |
-
}
|
33 |
-
|
34 |
-
return $is_refund;
|
35 |
-
}
|
36 |
-
|
37 |
-
public function get_refund_parent_id( $order ) {
|
38 |
-
if ( method_exists( $order, 'get_parent_id') ) { // WC3.0+
|
39 |
-
$parent_order_id = $order->get_parent_id();
|
40 |
-
} else {
|
41 |
-
$parent_order_id = wp_get_post_parent_id( WCX_Order::get_id( $order ) );
|
42 |
-
}
|
43 |
-
|
44 |
-
return $parent_order_id;
|
45 |
-
}
|
46 |
-
|
47 |
-
|
48 |
-
public function get_refund_parent( $order ) {
|
49 |
-
// only try if this is actually a refund
|
50 |
-
if ( ! $this->is_refund( $order ) ) {
|
51 |
-
return $order;
|
52 |
-
}
|
53 |
-
|
54 |
-
$parent_order_id = $this->get_refund_parent_id( $order );
|
55 |
-
$order = WCX::get_order( $parent_order_id );
|
56 |
-
return $order;
|
57 |
-
}
|
58 |
-
|
59 |
-
/**
|
60 |
-
* Check if billing address and shipping address are equal
|
61 |
-
*/
|
62 |
-
public function ships_to_different_address() {
|
63 |
-
// always prefer parent address for refunds
|
64 |
-
if ( $this->is_refund( $this->order ) ) {
|
65 |
-
$order = $this->get_refund_parent( $this->order );
|
66 |
-
} else {
|
67 |
-
$order = $this->order;
|
68 |
-
}
|
69 |
-
|
70 |
-
$address_comparison_fields = apply_filters( 'wpo_wcpdf_address_comparison_fields', array(
|
71 |
-
'first_name',
|
72 |
-
'last_name',
|
73 |
-
'company',
|
74 |
-
'address_1',
|
75 |
-
'address_2',
|
76 |
-
'city',
|
77 |
-
'state',
|
78 |
-
'postcode',
|
79 |
-
'country'
|
80 |
-
), $this );
|
81 |
-
|
82 |
-
foreach ($address_comparison_fields as $address_field) {
|
83 |
-
$billing_field = WCX_Order::get_prop( $order, "billing_{$address_field}", 'view');
|
84 |
-
$shipping_field = WCX_Order::get_prop( $order, "shipping_{$address_field}", 'view');
|
85 |
-
if ( $shipping_field != $billing_field ) {
|
86 |
-
// this address field is different -> ships to different address!
|
87 |
-
return true;
|
88 |
-
}
|
89 |
-
}
|
90 |
-
|
91 |
-
//if we got here, it means the addresses are equal -> doesn't ship to different address!
|
92 |
-
return apply_filters( 'wpo_wcpdf_ships_to_different_address', false, $order, $this );
|
93 |
-
}
|
94 |
-
|
95 |
-
/**
|
96 |
-
* Return/Show billing address
|
97 |
-
*/
|
98 |
-
public function get_billing_address() {
|
99 |
-
// always prefer parent billing address for refunds
|
100 |
-
if ( $this->is_refund( $this->order ) ) {
|
101 |
-
// temporarily switch order to make all filters / order calls work correctly
|
102 |
-
$refund = $this->order;
|
103 |
-
$this->order = $this->get_refund_parent( $this->order );
|
104 |
-
$address = apply_filters( 'wpo_wcpdf_billing_address', $this->order->get_formatted_billing_address(), $this );
|
105 |
-
// switch back & unset
|
106 |
-
$this->order = $refund;
|
107 |
-
unset($refund);
|
108 |
-
} elseif ( $address = $this->order->get_formatted_billing_address() ) {
|
109 |
-
// regular shop_order
|
110 |
-
$address = apply_filters( 'wpo_wcpdf_billing_address', $address, $this );
|
111 |
-
} else {
|
112 |
-
// no address
|
113 |
-
$address = apply_filters( 'wpo_wcpdf_billing_address', __('N/A', 'woocommerce-pdf-invoices-packing-slips' ), $this );
|
114 |
-
}
|
115 |
-
|
116 |
-
return $address;
|
117 |
-
}
|
118 |
-
public function billing_address() {
|
119 |
-
echo $this->get_billing_address();
|
120 |
-
}
|
121 |
-
|
122 |
-
/**
|
123 |
-
* Return/Show billing email
|
124 |
-
*/
|
125 |
-
public function get_billing_email() {
|
126 |
-
$billing_email = WCX_Order::get_prop( $this->order, 'billing_email', 'view' );
|
127 |
-
|
128 |
-
if ( !$billing_email && $this->is_refund( $this->order ) ) {
|
129 |
-
// try parent
|
130 |
-
$parent_order = $this->get_refund_parent( $this->order );
|
131 |
-
$billing_email = WCX_Order::get_prop( $parent_order, 'billing_email', 'view' );
|
132 |
-
}
|
133 |
-
|
134 |
-
return apply_filters( 'wpo_wcpdf_billing_email', $billing_email, $this );
|
135 |
-
}
|
136 |
-
public function billing_email() {
|
137 |
-
echo $this->get_billing_email();
|
138 |
-
}
|
139 |
-
|
140 |
-
/**
|
141 |
-
* Return/Show billing phone
|
142 |
-
*/
|
143 |
-
public function get_billing_phone() {
|
144 |
-
$billing_phone = WCX_Order::get_prop( $this->order, 'billing_phone', 'view' );
|
145 |
-
|
146 |
-
if ( !$billing_phone && $this->is_refund( $this->order ) ) {
|
147 |
-
// try parent
|
148 |
-
$parent_order = $this->get_refund_parent( $this->order );
|
149 |
-
$billing_phone = WCX_Order::get_prop( $parent_order, 'billing_phone', 'view' );
|
150 |
-
}
|
151 |
-
|
152 |
-
return apply_filters( 'wpo_wcpdf_billing_phone', $billing_phone, $this );
|
153 |
-
}
|
154 |
-
public function billing_phone() {
|
155 |
-
echo $this->get_billing_phone();
|
156 |
-
}
|
157 |
-
|
158 |
-
/**
|
159 |
-
* Return/Show shipping address
|
160 |
-
*/
|
161 |
-
public function get_shipping_address() {
|
162 |
-
// always prefer parent shipping address for refunds
|
163 |
-
if ( $this->is_refund( $this->order ) ) {
|
164 |
-
// temporarily switch order to make all filters / order calls work correctly
|
165 |
-
$refund = $this->order;
|
166 |
-
$this->order = $this->get_refund_parent( $this->order );
|
167 |
-
$address = apply_filters( 'wpo_wcpdf_shipping_address', $this->order->get_formatted_shipping_address(), $this );
|
168 |
-
// switch back & unset
|
169 |
-
$this->order = $refund;
|
170 |
-
unset($refund);
|
171 |
-
} elseif ( $address = $this->order->get_formatted_shipping_address() ) {
|
172 |
-
// regular shop_order
|
173 |
-
$address = apply_filters( 'wpo_wcpdf_shipping_address', $address, $this );
|
174 |
-
} else {
|
175 |
-
// no address
|
176 |
-
$address = apply_filters( 'wpo_wcpdf_shipping_address', __('N/A', 'woocommerce-pdf-invoices-packing-slips' ), $this );
|
177 |
-
}
|
178 |
-
|
179 |
-
return $address;
|
180 |
-
}
|
181 |
-
public function shipping_address() {
|
182 |
-
echo $this->get_shipping_address();
|
183 |
-
}
|
184 |
-
|
185 |
-
/**
|
186 |
-
* Return/Show a custom field
|
187 |
-
*/
|
188 |
-
public function get_custom_field( $field_name ) {
|
189 |
-
$custom_field = WCX_Order::get_meta( $this->order, $field_name, true );
|
190 |
-
// if not found, try prefixed with underscore
|
191 |
-
if ( !$custom_field && substr( $field_name, 0, 1 ) !== '_' ) {
|
192 |
-
$custom_field = WCX_Order::get_meta( $this->order, "_{$field_name}", true );
|
193 |
-
}
|
194 |
-
|
195 |
-
// WC3.0 fallback to properties
|
196 |
-
$property = str_replace('-', '_', sanitize_title( ltrim($field_name, '_') ) );
|
197 |
-
if ( !$custom_field && is_callable( array( $this->order, "get_{$property}" ) ) ) {
|
198 |
-
$custom_field = $this->order->{"get_{$property}"}( 'view' );
|
199 |
-
}
|
200 |
-
|
201 |
-
// fallback to parent for refunds
|
202 |
-
if ( !$custom_field && $this->is_refund( $this->order ) ) {
|
203 |
-
$parent_order = $this->get_refund_parent( $this->order );
|
204 |
-
$custom_field = WCX_Order::get_meta( $parent_order, $field_name, true );
|
205 |
-
|
206 |
-
// WC3.0 fallback to properties
|
207 |
-
if ( !$custom_field && is_callable( array( $parent_order, "get_{$property}" ) ) ) {
|
208 |
-
$custom_field = $parent_order->{"get_{$property}"}( 'view' );
|
209 |
-
}
|
210 |
-
}
|
211 |
-
|
212 |
-
return apply_filters( 'wpo_wcpdf_billing_custom_field', $custom_field, $this );
|
213 |
-
}
|
214 |
-
public function custom_field( $field_name, $field_label = '', $display_empty = false ) {
|
215 |
-
$custom_field = $this->get_custom_field( $field_name );
|
216 |
-
if (!empty($field_label)){
|
217 |
-
// add a a trailing space to the label
|
218 |
-
$field_label .= ' ';
|
219 |
-
}
|
220 |
-
|
221 |
-
if (!empty($custom_field) || $display_empty) {
|
222 |
-
echo $field_label . nl2br ($custom_field);
|
223 |
-
}
|
224 |
-
}
|
225 |
-
|
226 |
-
/**
|
227 |
-
* Return/show product attribute
|
228 |
-
*/
|
229 |
-
public function get_product_attribute( $attribute_name, $product ) {
|
230 |
-
// first, check the text attributes
|
231 |
-
$attributes = $product->get_attributes();
|
232 |
-
$attribute_key = @wc_attribute_taxonomy_name( $attribute_name );
|
233 |
-
if (array_key_exists( sanitize_title( $attribute_name ), $attributes) ) {
|
234 |
-
$attribute = $product->get_attribute ( $attribute_name );
|
235 |
-
} elseif (array_key_exists( sanitize_title( $attribute_key ), $attributes) ) {
|
236 |
-
$attribute = $product->get_attribute ( $attribute_key );
|
237 |
-
}
|
238 |
-
|
239 |
-
if (empty($attribute)) {
|
240 |
-
// not a text attribute, try attribute taxonomy
|
241 |
-
$attribute_key = @wc_attribute_taxonomy_name( $attribute_name );
|
242 |
-
$product_id = WCX_Product::get_prop($product, 'id');
|
243 |
-
$product_terms = @wc_get_product_terms( $product_id, $attribute_key, array( 'fields' => 'names' ) );
|
244 |
-
// check if not empty, then display
|
245 |
-
if ( !empty($product_terms) ) {
|
246 |
-
$attribute = array_shift( $product_terms );
|
247 |
-
}
|
248 |
-
}
|
249 |
-
|
250 |
-
// WC3.0+ fallback parent product for variations
|
251 |
-
if ( empty($attribute) && version_compare( WOOCOMMERCE_VERSION, '3.0', '>=' ) && $product->is_type( 'variation' ) ) {
|
252 |
-
$product = wc_get_product( $product->get_parent_id() );
|
253 |
-
$attribute = $this->get_product_attribute( $attribute_name, $product );
|
254 |
-
}
|
255 |
-
|
256 |
-
return isset($attribute) ? $attribute : false;
|
257 |
-
}
|
258 |
-
public function product_attribute( $attribute_name, $product ) {
|
259 |
-
echo $this->get_product_attribute( $attribute_name, $product );
|
260 |
-
}
|
261 |
-
|
262 |
-
/**
|
263 |
-
* Return/Show order notes
|
264 |
-
* could use $order->get_customer_order_notes(), but that filters out private notes already
|
265 |
-
*/
|
266 |
-
public function get_order_notes( $filter = 'customer' ) {
|
267 |
-
if ( $this->is_refund( $this->order ) ) {
|
268 |
-
$post_id = $this->get_refund_parent_id( $this->order );
|
269 |
-
} else {
|
270 |
-
$post_id = $this->order_id;
|
271 |
-
}
|
272 |
-
|
273 |
-
if ( empty( $post_id ) ) {
|
274 |
-
return; // prevent order notes from all orders showing when document is not loaded properly
|
275 |
-
}
|
276 |
-
|
277 |
-
$args = array(
|
278 |
-
'post_id' => $post_id,
|
279 |
-
'approve' => 'approve',
|
280 |
-
'type' => 'order_note'
|
281 |
-
);
|
282 |
-
|
283 |
-
remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
|
284 |
-
|
285 |
-
$notes = get_comments( $args );
|
286 |
-
|
287 |
-
add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
|
288 |
-
|
289 |
-
if ( $notes ) {
|
290 |
-
foreach( $notes as $key => $note ) {
|
291 |
-
if ( $filter == 'customer' && !get_comment_meta( $note->comment_ID, 'is_customer_note', true ) ) {
|
292 |
-
unset($notes[$key]);
|
293 |
-
}
|
294 |
-
if ( $filter == 'private' && get_comment_meta( $note->comment_ID, 'is_customer_note', true ) ) {
|
295 |
-
unset($notes[$key]);
|
296 |
-
}
|
297 |
-
}
|
298 |
-
return $notes;
|
299 |
-
}
|
300 |
-
}
|
301 |
-
public function order_notes( $filter = 'customer' ) {
|
302 |
-
$notes = $this->get_order_notes( $filter );
|
303 |
-
if ( $notes ) {
|
304 |
-
foreach( $notes as $note ) {
|
305 |
-
?>
|
306 |
-
<div class="note_content">
|
307 |
-
<?php echo wpautop( wptexturize( wp_kses_post( $note->comment_content ) ) ); ?>
|
308 |
-
</div>
|
309 |
-
<?php
|
310 |
-
}
|
311 |
-
}
|
312 |
-
}
|
313 |
-
|
314 |
-
/**
|
315 |
-
* Return/Show the current date
|
316 |
-
*/
|
317 |
-
public function get_current_date() {
|
318 |
-
return apply_filters( 'wpo_wcpdf_date', date_i18n( get_option( 'date_format' ) ) );
|
319 |
-
}
|
320 |
-
public function current_date() {
|
321 |
-
echo $this->get_current_date();
|
322 |
-
}
|
323 |
-
|
324 |
-
/**
|
325 |
-
* Return/Show payment method
|
326 |
-
*/
|
327 |
-
public function get_payment_method() {
|
328 |
-
$payment_method_label = __( 'Payment method', 'woocommerce-pdf-invoices-packing-slips' );
|
329 |
-
|
330 |
-
if ( $this->is_refund( $this->order ) ) {
|
331 |
-
$parent_order = $this->get_refund_parent( $this->order );
|
332 |
-
$payment_method_title = WCX_Order::get_prop( $parent_order, 'payment_method_title', 'view' );
|
333 |
-
} else {
|
334 |
-
$payment_method_title = WCX_Order::get_prop( $this->order, 'payment_method_title', 'view' );
|
335 |
-
}
|
336 |
-
|
337 |
-
$payment_method = __( $payment_method_title, 'woocommerce' );
|
338 |
-
|
339 |
-
return apply_filters( 'wpo_wcpdf_payment_method', $payment_method, $this );
|
340 |
-
}
|
341 |
-
public function payment_method() {
|
342 |
-
echo $this->get_payment_method();
|
343 |
-
}
|
344 |
-
|
345 |
-
/**
|
346 |
-
* Return/Show shipping method
|
347 |
-
*/
|
348 |
-
public function get_shipping_method() {
|
349 |
-
$shipping_method_label = __( 'Shipping method', 'woocommerce-pdf-invoices-packing-slips' );
|
350 |
-
$shipping_method = __( $this->order->get_shipping_method(), 'woocommerce' );
|
351 |
-
return apply_filters( 'wpo_wcpdf_shipping_method', $shipping_method, $this );
|
352 |
-
}
|
353 |
-
public function shipping_method() {
|
354 |
-
echo $this->get_shipping_method();
|
355 |
-
}
|
356 |
-
|
357 |
-
/**
|
358 |
-
* Return/Show order number
|
359 |
-
*/
|
360 |
-
public function get_order_number() {
|
361 |
-
// try parent first
|
362 |
-
if ( $this->is_refund( $this->order ) ) {
|
363 |
-
$parent_order = $this->get_refund_parent( $this->order );
|
364 |
-
$order_number = $parent_order->get_order_number();
|
365 |
-
} else {
|
366 |
-
$order_number = $this->order->get_order_number();
|
367 |
-
}
|
368 |
-
|
369 |
-
// Trim the hash to have a clean number but still
|
370 |
-
// support any filters that were applied before.
|
371 |
-
$order_number = ltrim($order_number, '#');
|
372 |
-
return apply_filters( 'wpo_wcpdf_order_number', $order_number, $this );
|
373 |
-
}
|
374 |
-
public function order_number() {
|
375 |
-
echo $this->get_order_number();
|
376 |
-
}
|
377 |
-
|
378 |
-
/**
|
379 |
-
* Return/Show the order date
|
380 |
-
*/
|
381 |
-
public function get_order_date() {
|
382 |
-
if ( $this->is_refund( $this->order ) ) {
|
383 |
-
$parent_order = $this->get_refund_parent( $this->order );
|
384 |
-
$order_date = WCX_Order::get_prop( $parent_order, 'date_created' );
|
385 |
-
} else {
|
386 |
-
$order_date = WCX_Order::get_prop( $this->order, 'date_created' );
|
387 |
-
}
|
388 |
-
|
389 |
-
$date = $order_date->date_i18n( get_option( 'date_format' ) );
|
390 |
-
$mysql_date = $order_date->date( "Y-m-d H:i:s" );
|
391 |
-
return apply_filters( 'wpo_wcpdf_order_date', $date, $mysql_date, $this );
|
392 |
-
}
|
393 |
-
public function order_date() {
|
394 |
-
echo $this->get_order_date();
|
395 |
-
}
|
396 |
-
|
397 |
-
/**
|
398 |
-
* Return the order items
|
399 |
-
*/
|
400 |
-
public function get_order_items() {
|
401 |
-
$items = $this->order->get_items();
|
402 |
-
$data_list = array();
|
403 |
-
|
404 |
-
if( sizeof( $items ) > 0 ) {
|
405 |
-
foreach ( $items as $item_id => $item ) {
|
406 |
-
// Array with data for the pdf template
|
407 |
-
$data = array();
|
408 |
-
|
409 |
-
// Set the item_id
|
410 |
-
$data['item_id'] = $item_id;
|
411 |
-
|
412 |
-
// Set the id
|
413 |
-
$data['product_id'] = $item['product_id'];
|
414 |
-
$data['variation_id'] = $item['variation_id'];
|
415 |
-
|
416 |
-
// Set item name
|
417 |
-
$data['name'] = $item['name'];
|
418 |
-
|
419 |
-
// Set item quantity
|
420 |
-
$data['quantity'] = $item['qty'];
|
421 |
-
|
422 |
-
// Set the line total (=after discount)
|
423 |
-
$data['line_total'] = $this->format_price( $item['line_total'] );
|
424 |
-
$data['single_line_total'] = $this->format_price( $item['line_total'] / max( 1, abs( $item['qty'] ) ) );
|
425 |
-
$data['line_tax'] = $this->format_price( $item['line_tax'] );
|
426 |
-
$data['single_line_tax'] = $this->format_price( $item['line_tax'] / max( 1, abs( $item['qty'] ) ) );
|
427 |
-
|
428 |
-
$line_tax_data = maybe_unserialize( isset( $item['line_tax_data'] ) ? $item['line_tax_data'] : '' );
|
429 |
-
$data['tax_rates'] = $this->get_tax_rate( $item['tax_class'], $item['line_total'], $item['line_tax'], $line_tax_data );
|
430 |
-
|
431 |
-
// Set the line subtotal (=before discount)
|
432 |
-
$data['line_subtotal'] = $this->format_price( $item['line_subtotal'] );
|
433 |
-
$data['line_subtotal_tax'] = $this->format_price( $item['line_subtotal_tax'] );
|
434 |
-
$data['ex_price'] = $this->get_formatted_item_price( $item, 'total', 'excl' );
|
435 |
-
$data['price'] = $this->get_formatted_item_price( $item, 'total' );
|
436 |
-
$data['order_price'] = $this->order->get_formatted_line_subtotal( $item ); // formatted according to WC settings
|
437 |
-
|
438 |
-
// Calculate the single price with the same rules as the formatted line subtotal (!)
|
439 |
-
// = before discount
|
440 |
-
$data['ex_single_price'] = $this->get_formatted_item_price( $item, 'single', 'excl' );
|
441 |
-
$data['single_price'] = $this->get_formatted_item_price( $item, 'single' );
|
442 |
-
|
443 |
-
// Pass complete item array
|
444 |
-
$data['item'] = $item;
|
445 |
-
|
446 |
-
// Get the product to add more info
|
447 |
-
$product = $this->order->get_product_from_item( $item );
|
448 |
-
|
449 |
-
// Checking fo existance, thanks to MDesigner0
|
450 |
-
if( !empty( $product ) ) {
|
451 |
-
// Thumbnail (full img tag)
|
452 |
-
$data['thumbnail'] = $this->get_thumbnail( $product );
|
453 |
-
|
454 |
-
// Set item SKU
|
455 |
-
$data['sku'] = $product->get_sku();
|
456 |
-
|
457 |
-
// Set item weight
|
458 |
-
$data['weight'] = $product->get_weight();
|
459 |
-
|
460 |
-
// Set item dimensions
|
461 |
-
$data['dimensions'] = WCX_Product::get_dimensions( $product );
|
462 |
-
|
463 |
-
// Pass complete product object
|
464 |
-
$data['product'] = $product;
|
465 |
-
|
466 |
-
} else {
|
467 |
-
$data['product'] = null;
|
468 |
-
}
|
469 |
-
|
470 |
-
// Set item meta
|
471 |
-
if (function_exists('wc_display_item_meta')) { // WC3.0+
|
472 |
-
$data['meta'] = wc_display_item_meta( $item, array(
|
473 |
-
'echo' => false,
|
474 |
-
) );
|
475 |
-
} else {
|
476 |
-
if ( version_compare( WOOCOMMERCE_VERSION, '2.4', '<' ) ) {
|
477 |
-
$meta = new \WC_Order_Item_Meta( $item['item_meta'], $product );
|
478 |
-
} else { // pass complete item for WC2.4+
|
479 |
-
$meta = new \WC_Order_Item_Meta( $item, $product );
|
480 |
-
}
|
481 |
-
$data['meta'] = $meta->display( false, true );
|
482 |
-
}
|
483 |
-
|
484 |
-
$data_list[$item_id] = apply_filters( 'wpo_wcpdf_order_item_data', $data, $this->order, $this->get_type() );
|
485 |
-
}
|
486 |
-
}
|
487 |
-
|
488 |
-
return apply_filters( 'wpo_wcpdf_order_items_data', $data_list, $this->order, $this->get_type() );
|
489 |
-
}
|
490 |
-
|
491 |
-
/**
|
492 |
-
* Get the tax rates/percentages for a given tax class
|
493 |
-
* @param string $tax_class tax class slug
|
494 |
-
* @return string $tax_rates imploded list of tax rates
|
495 |
-
*/
|
496 |
-
public function get_tax_rate( $tax_class, $line_total, $line_tax, $line_tax_data = '' ) {
|
497 |
-
// first try the easy wc2.2+ way, using line_tax_data
|
498 |
-
if ( !empty( $line_tax_data ) && isset($line_tax_data['total']) ) {
|
499 |
-
$tax_rates = array();
|
500 |
-
|
501 |
-
$line_taxes = $line_tax_data['subtotal'];
|
502 |
-
foreach ( $line_taxes as $tax_id => $tax ) {
|
503 |
-
if ( isset($tax) && $tax !== '' ) {
|
504 |
-
$tax_rates[] = $this->get_tax_rate_by_id( $tax_id ) . ' %';
|
505 |
-
}
|
506 |
-
}
|
507 |
-
|
508 |
-
$tax_rates = implode(' ,', $tax_rates );
|
509 |
-
return $tax_rates;
|
510 |
-
}
|
511 |
-
|
512 |
-
if ( $line_tax == 0 ) {
|
513 |
-
return '-'; // no need to determine tax rate...
|
514 |
-
}
|
515 |
-
|
516 |
-
if ( version_compare( WOOCOMMERCE_VERSION, '2.1' ) >= 0 && !apply_filters( 'wpo_wcpdf_calculate_tax_rate', false ) ) {
|
517 |
-
// WC 2.1 or newer is used
|
518 |
-
$tax = new \WC_Tax();
|
519 |
-
$taxes = $tax->get_rates( $tax_class );
|
520 |
-
|
521 |
-
$tax_rates = array();
|
522 |
-
|
523 |
-
foreach ($taxes as $tax) {
|
524 |
-
$tax_rates[$tax['label']] = round( $tax['rate'], 2 ).' %';
|
525 |
-
}
|
526 |
-
|
527 |
-
if (empty($tax_rates)) {
|
528 |
-
// one last try: manually calculate
|
529 |
-
if ( $line_total != 0) {
|
530 |
-
$tax_rates[] = round( ($line_tax / $line_total)*100, 1 ).' %';
|
531 |
-
} else {
|
532 |
-
$tax_rates[] = '-';
|
533 |
-
}
|
534 |
-
}
|
535 |
-
|
536 |
-
$tax_rates = implode(' ,', $tax_rates );
|
537 |
-
} else {
|
538 |
-
// Backwards compatibility/fallback: calculate tax from line items
|
539 |
-
if ( $line_total != 0) {
|
540 |
-
$tax_rates = round( ($line_tax / $line_total)*100, 1 ).' %';
|
541 |
-
} else {
|
542 |
-
$tax_rates = '-';
|
543 |
-
}
|
544 |
-
}
|
545 |
-
|
546 |
-
return $tax_rates;
|
547 |
-
}
|
548 |
-
|
549 |
-
/**
|
550 |
-
* Returns the percentage rate (float) for a given tax rate ID.
|
551 |
-
* @param int $rate_id woocommerce tax rate id
|
552 |
-
* @return float $rate percentage rate
|
553 |
-
*/
|
554 |
-
public function get_tax_rate_by_id( $rate_id ) {
|
555 |
-
global $wpdb;
|
556 |
-
$rate = $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %d;", $rate_id ) );
|
557 |
-
return (float) $rate;
|
558 |
-
}
|
559 |
-
|
560 |
-
/**
|
561 |
-
* Returns a an array with rate_id => tax rate data (array) of all tax rates in woocommerce
|
562 |
-
* @return array $tax_rate_ids keyed by id
|
563 |
-
*/
|
564 |
-
public function get_tax_rate_ids() {
|
565 |
-
global $wpdb;
|
566 |
-
$rates = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}woocommerce_tax_rates" );
|
567 |
-
|
568 |
-
$tax_rate_ids = array();
|
569 |
-
foreach ($rates as $rate) {
|
570 |
-
// var_dump($rate->tax_rate_id);
|
571 |
-
// die($rate);
|
572 |
-
$rate_id = $rate->tax_rate_id;
|
573 |
-
unset($rate->tax_rate_id);
|
574 |
-
$tax_rate_ids[$rate_id] = (array) $rate;
|
575 |
-
}
|
576 |
-
|
577 |
-
return $tax_rate_ids;
|
578 |
-
}
|
579 |
-
|
580 |
-
/**
|
581 |
-
* Returns the main product image ID
|
582 |
-
* Adapted from the WC_Product class
|
583 |
-
* (does not support thumbnail sizes)
|
584 |
-
*
|
585 |
-
* @access public
|
586 |
-
* @return string
|
587 |
-
*/
|
588 |
-
public function get_thumbnail_id ( $product ) {
|
589 |
-
global $woocommerce;
|
590 |
-
|
591 |
-
$product_id = WCX_Product::get_id( $product );
|
592 |
-
|
593 |
-
if ( has_post_thumbnail( $product_id ) ) {
|
594 |
-
$thumbnail_id = get_post_thumbnail_id ( $product_id );
|
595 |
-
} elseif ( ( $parent_id = wp_get_post_parent_id( $product_id ) ) && has_post_thumbnail( $parent_id ) ) {
|
596 |
-
$thumbnail_id = get_post_thumbnail_id ( $parent_id );
|
597 |
-
} else {
|
598 |
-
$thumbnail_id = false;
|
599 |
-
}
|
600 |
-
|
601 |
-
return $thumbnail_id;
|
602 |
-
}
|
603 |
-
|
604 |
-
/**
|
605 |
-
* Returns the thumbnail image tag
|
606 |
-
*
|
607 |
-
* uses the internal WooCommerce/WP functions and extracts the image url or path
|
608 |
-
* rather than the thumbnail ID, to simplify the code and make it possible to
|
609 |
-
* filter for different thumbnail sizes
|
610 |
-
*
|
611 |
-
* @access public
|
612 |
-
* @return string
|
613 |
-
*/
|
614 |
-
public function get_thumbnail ( $product ) {
|
615 |
-
// Get default WooCommerce img tag (url/http)
|
616 |
-
$size = apply_filters( 'wpo_wcpdf_thumbnail_size', 'shop_thumbnail' );
|
617 |
-
$thumbnail_img_tag_url = $product->get_image( $size, array( 'title' => '' ) );
|
618 |
-
|
619 |
-
// Extract the url from img
|
620 |
-
preg_match('/<img(.*)src(.*)=(.*)"(.*)"/U', $thumbnail_img_tag_url, $thumbnail_url );
|
621 |
-
$thumbnail_url = array_pop($thumbnail_url);
|
622 |
-
// remove http/https from image tag url to avoid mixed origin conflicts
|
623 |
-
$contextless_thumbnail_url = ltrim( str_replace(array('http://','https://'), '', $thumbnail_url ), '/' );
|
624 |
-
|
625 |
-
// convert url to path
|
626 |
-
if ( defined('WP_CONTENT_DIR') && strpos( WP_CONTENT_DIR, ABSPATH ) !== false ) {
|
627 |
-
$forwardslash_basepath = str_replace('\\','/', ABSPATH);
|
628 |
-
$contextless_site_url = str_replace(array('http://','https://'), '', trailingslashit(get_site_url()));
|
629 |
-
} else {
|
630 |
-
// bedrock e.a
|
631 |
-
$forwardslash_basepath = str_replace('\\','/', WP_CONTENT_DIR);
|
632 |
-
$contextless_site_url = str_replace(array('http://','https://'), '', trailingslashit(WP_CONTENT_URL));
|
633 |
-
}
|
634 |
-
$thumbnail_path = str_replace( $contextless_site_url, trailingslashit( $forwardslash_basepath ), $contextless_thumbnail_url);
|
635 |
-
|
636 |
-
// fallback if thumbnail file doesn't exist
|
637 |
-
if (apply_filters('wpo_wcpdf_use_path', true) && !file_exists($thumbnail_path)) {
|
638 |
-
if ($thumbnail_id = $this->get_thumbnail_id( $product ) ) {
|
639 |
-
$thumbnail_path = get_attached_file( $thumbnail_id );
|
640 |
-
}
|
641 |
-
}
|
642 |
-
|
643 |
-
// Thumbnail (full img tag)
|
644 |
-
if ( apply_filters('wpo_wcpdf_use_path', true) && file_exists($thumbnail_path) ) {
|
645 |
-
// load img with server path by default
|
646 |
-
$thumbnail = sprintf('<img width="90" height="90" src="%s" class="attachment-shop_thumbnail wp-post-image">', $thumbnail_path );
|
647 |
-
} elseif ( apply_filters('wpo_wcpdf_use_path', true) && !file_exists($thumbnail_path) ) {
|
648 |
-
// should use paths but file not found, replace // with http(s):// for dompdf compatibility
|
649 |
-
if ( substr( $thumbnail_url, 0, 2 ) === "//" ) {
|
650 |
-
$prefix = is_ssl() ? 'https://' : 'http://';
|
651 |
-
$https_thumbnail_url = $prefix . ltrim( $thumbnail_url, '/' );
|
652 |
-
}
|
653 |
-
$thumbnail_img_tag_url = str_replace($thumbnail_url, $https_thumbnail_url, $thumbnail_img_tag_url);
|
654 |
-
$thumbnail = $thumbnail_img_tag_url;
|
655 |
-
} else {
|
656 |
-
// load img with http url when filtered
|
657 |
-
$thumbnail = $thumbnail_img_tag_url;
|
658 |
-
}
|
659 |
-
|
660 |
-
// die($thumbnail);
|
661 |
-
return $thumbnail;
|
662 |
-
}
|
663 |
-
|
664 |
-
/**
|
665 |
-
* Return the order totals listing
|
666 |
-
*/
|
667 |
-
public function get_woocommerce_totals() {
|
668 |
-
// get totals and remove the semicolon
|
669 |
-
$totals = apply_filters( 'wpo_wcpdf_raw_order_totals', $this->order->get_order_item_totals(), $this->order );
|
670 |
-
|
671 |
-
// remove the colon for every label
|
672 |
-
foreach ( $totals as $key => $total ) {
|
673 |
-
$label = $total['label'];
|
674 |
-
$colon = strrpos( $label, ':' );
|
675 |
-
if( $colon !== false ) {
|
676 |
-
$label = substr_replace( $label, '', $colon, 1 );
|
677 |
-
}
|
678 |
-
$totals[$key]['label'] = $label;
|
679 |
-
}
|
680 |
-
|
681 |
-
// WC2.4 fix order_total for refunded orders
|
682 |
-
// not if this is the actual refund!
|
683 |
-
if ( ! $this->is_refund( $this->order ) ) {
|
684 |
-
$total_refunded = method_exists($this->order, 'get_total_refunded') ? $this->order->get_total_refunded() : 0;
|
685 |
-
if ( version_compare( WOOCOMMERCE_VERSION, '2.4', '>=' ) && isset($totals['order_total']) && $total_refunded ) {
|
686 |
-
if ( version_compare( WOOCOMMERCE_VERSION, '3.0', '>=' ) ) {
|
687 |
-
$tax_display = get_option( 'woocommerce_tax_display_cart' );
|
688 |
-
} else {
|
689 |
-
$tax_display = WCX_Order::get_prop( $this->order, 'tax_display_cart' );
|
690 |
-
}
|
691 |
-
|
692 |
-
$totals['order_total']['value'] = wc_price( $this->order->get_total(), array( 'currency' => WCX_Order::get_prop( $this->order, 'currency' ) ) );
|
693 |
-
$order_total = $this->order->get_total();
|
694 |
-
$tax_string = '';
|
695 |
-
|
696 |
-
// Tax for inclusive prices
|
697 |
-
if ( wc_tax_enabled() && 'incl' == $tax_display ) {
|
698 |
-
$tax_string_array = array();
|
699 |
-
if ( 'itemized' == get_option( 'woocommerce_tax_total_display' ) ) {
|
700 |
-
foreach ( $this->order->get_tax_totals() as $code => $tax ) {
|
701 |
-
$tax_amount = $tax->formatted_amount;
|
702 |
-
$tax_string_array[] = sprintf( '%s %s', $tax_amount, $tax->label );
|
703 |
-
}
|
704 |
-
} else {
|
705 |
-
$tax_string_array[] = sprintf( '%s %s', wc_price( $this->order->get_total_tax() - $this->order->get_total_tax_refunded(), array( 'currency' => WCX_Order::get_prop( $this->order, 'currency' ) ) ), WC()->countries->tax_or_vat() );
|
706 |
-
}
|
707 |
-
if ( ! empty( $tax_string_array ) ) {
|
708 |
-
if ( version_compare( WOOCOMMERCE_VERSION, '2.6', '>=' ) ) {
|
709 |
-
$tax_string = ' ' . sprintf( __( '(includes %s)', 'woocommerce' ), implode( ', ', $tax_string_array ) );
|
710 |
-
} else {
|
711 |
-
// use old capitalized string
|
712 |
-
$tax_string = ' ' . sprintf( __( '(Includes %s)', 'woocommerce' ), implode( ', ', $tax_string_array ) );
|
713 |
-
}
|
714 |
-
}
|
715 |
-
}
|
716 |
-
|
717 |
-
$totals['order_total']['value'] .= $tax_string;
|
718 |
-
}
|
719 |
-
|
720 |
-
// remove refund lines (shouldn't be in invoice)
|
721 |
-
foreach ( $totals as $key => $total ) {
|
722 |
-
if ( strpos($key, 'refund_') !== false ) {
|
723 |
-
unset( $totals[$key] );
|
724 |
-
}
|
725 |
-
}
|
726 |
-
|
727 |
-
}
|
728 |
-
|
729 |
-
return apply_filters( 'wpo_wcpdf_woocommerce_totals', $totals, $this->order, $this->get_type() );
|
730 |
-
}
|
731 |
-
|
732 |
-
/**
|
733 |
-
* Return/show the order subtotal
|
734 |
-
*/
|
735 |
-
public function get_order_subtotal( $tax = 'excl', $discount = 'incl' ) { // set $tax to 'incl' to include tax, same for $discount
|
736 |
-
//$compound = ($discount == 'incl')?true:false;
|
737 |
-
$subtotal = $this->order->get_subtotal_to_display( false, $tax );
|
738 |
-
|
739 |
-
$subtotal = ($pos = strpos($subtotal, ' <small')) ? substr($subtotal, 0, $pos) : $subtotal; //removing the 'excluding tax' text
|
740 |
-
|
741 |
-
$subtotal = array (
|
742 |
-
'label' => __('Subtotal', 'woocommerce-pdf-invoices-packing-slips' ),
|
743 |
-
'value' => $subtotal,
|
744 |
-
);
|
745 |
-
|
746 |
-
return apply_filters( 'wpo_wcpdf_order_subtotal', $subtotal, $tax, $discount, $this );
|
747 |
-
}
|
748 |
-
public function order_subtotal( $tax = 'excl', $discount = 'incl' ) {
|
749 |
-
$subtotal = $this->get_order_subtotal( $tax, $discount );
|
750 |
-
echo $subtotal['value'];
|
751 |
-
}
|
752 |
-
|
753 |
-
/**
|
754 |
-
* Return/show the order shipping costs
|
755 |
-
*/
|
756 |
-
public function get_order_shipping( $tax = 'excl' ) { // set $tax to 'incl' to include tax
|
757 |
-
$shipping_cost = WCX_Order::get_prop( $this->order, 'shipping_total', 'view' );
|
758 |
-
$shipping_tax = WCX_Order::get_prop( $this->order, 'shipping_tax', 'view' );
|
759 |
-
|
760 |
-
if ($tax == 'excl' ) {
|
761 |
-
$formatted_shipping_cost = $this->format_price( $shipping_cost );
|
762 |
-
} else {
|
763 |
-
$formatted_shipping_cost = $this->format_price( $shipping_cost + $shipping_tax );
|
764 |
-
}
|
765 |
-
|
766 |
-
$shipping = array (
|
767 |
-
'label' => __('Shipping', 'woocommerce-pdf-invoices-packing-slips' ),
|
768 |
-
'value' => $formatted_shipping_cost,
|
769 |
-
'tax' => $this->format_price( $shipping_tax ),
|
770 |
-
);
|
771 |
-
return apply_filters( 'wpo_wcpdf_order_shipping', $shipping, $tax, $this );
|
772 |
-
}
|
773 |
-
public function order_shipping( $tax = 'excl' ) {
|
774 |
-
$shipping = $this->get_order_shipping( $tax );
|
775 |
-
echo $shipping['value'];
|
776 |
-
}
|
777 |
-
|
778 |
-
/**
|
779 |
-
* Return/show the total discount
|
780 |
-
*/
|
781 |
-
public function get_order_discount( $type = 'total', $tax = 'incl' ) {
|
782 |
-
if ( $tax == 'incl' ) {
|
783 |
-
switch ($type) {
|
784 |
-
case 'cart':
|
785 |
-
// Cart Discount - pre-tax discounts. (deprecated in WC2.3)
|
786 |
-
$discount_value = $this->order->get_cart_discount();
|
787 |
-
break;
|
788 |
-
case 'order':
|
789 |
-
// Order Discount - post-tax discounts. (deprecated in WC2.3)
|
790 |
-
$discount_value = $this->order->get_order_discount();
|
791 |
-
break;
|
792 |
-
case 'total':
|
793 |
-
// Total Discount
|
794 |
-
if ( version_compare( WOOCOMMERCE_VERSION, '2.3' ) >= 0 ) {
|
795 |
-
$discount_value = $this->order->get_total_discount( false ); // $ex_tax = false
|
796 |
-
} else {
|
797 |
-
// WC2.2 and older: recalculate to include tax
|
798 |
-
$discount_value = 0;
|
799 |
-
$items = $this->order->get_items();;
|
800 |
-
if( sizeof( $items ) > 0 ) {
|
801 |
-
foreach( $items as $item ) {
|
802 |
-
$discount_value += ($item['line_subtotal'] + $item['line_subtotal_tax']) - ($item['line_total'] + $item['line_tax']);
|
803 |
-
}
|
804 |
-
}
|
805 |
-
}
|
806 |
-
|
807 |
-
break;
|
808 |
-
default:
|
809 |
-
// Total Discount - Cart & Order Discounts combined
|
810 |
-
$discount_value = $this->order->get_total_discount();
|
811 |
-
break;
|
812 |
-
}
|
813 |
-
} else { // calculate discount excluding tax
|
814 |
-
if ( version_compare( WOOCOMMERCE_VERSION, '2.3' ) >= 0 ) {
|
815 |
-
$discount_value = $this->order->get_total_discount( true ); // $ex_tax = true
|
816 |
-
} else {
|
817 |
-
// WC2.2 and older: recalculate to exclude tax
|
818 |
-
$discount_value = 0;
|
819 |
-
|
820 |
-
$items = $this->order->get_items();;
|
821 |
-
if( sizeof( $items ) > 0 ) {
|
822 |
-
foreach( $items as $item ) {
|
823 |
-
$discount_value += ($item['line_subtotal'] - $item['line_total']);
|
824 |
-
}
|
825 |
-
}
|
826 |
-
}
|
827 |
-
}
|
828 |
-
|
829 |
-
$discount = array (
|
830 |
-
'label' => __('Discount', 'woocommerce-pdf-invoices-packing-slips' ),
|
831 |
-
'value' => $this->format_price( $discount_value ),
|
832 |
-
'raw_value' => $discount_value,
|
833 |
-
);
|
834 |
-
|
835 |
-
if ( round( $discount_value, 3 ) != 0 ) {
|
836 |
-
return apply_filters( 'wpo_wcpdf_order_discount', $discount, $type, $tax, $this );
|
837 |
-
}
|
838 |
-
}
|
839 |
-
public function order_discount( $type = 'total', $tax = 'incl' ) {
|
840 |
-
$discount = $this->get_order_discount( $type, $tax );
|
841 |
-
echo $discount['value'];
|
842 |
-
}
|
843 |
-
|
844 |
-
/**
|
845 |
-
* Return the order fees
|
846 |
-
*/
|
847 |
-
public function get_order_fees( $tax = 'excl' ) {
|
848 |
-
if ( $_fees = $this->order->get_fees() ) {
|
849 |
-
foreach( $_fees as $id => $fee ) {
|
850 |
-
if ($tax == 'excl' ) {
|
851 |
-
$fee_price = $this->format_price( $fee['line_total'] );
|
852 |
-
} else {
|
853 |
-
$fee_price = $this->format_price( $fee['line_total'] + $fee['line_tax'] );
|
854 |
-
}
|
855 |
-
|
856 |
-
$fees[ $id ] = array(
|
857 |
-
'label' => $fee['name'],
|
858 |
-
'value' => $fee_price,
|
859 |
-
'line_total' => $this->format_price( $fee['line_total'] ),
|
860 |
-
'line_tax' => $this->format_price( $fee['line_tax'] )
|
861 |
-
);
|
862 |
-
}
|
863 |
-
return $fees;
|
864 |
-
}
|
865 |
-
}
|
866 |
-
|
867 |
-
/**
|
868 |
-
* Return the order taxes
|
869 |
-
*/
|
870 |
-
public function get_order_taxes() {
|
871 |
-
$tax_label = __( 'VAT', 'woocommerce-pdf-invoices-packing-slips' ); // register alternate label translation
|
872 |
-
$tax_label = __( 'Tax rate', 'woocommerce-pdf-invoices-packing-slips' );
|
873 |
-
$tax_rate_ids = $this->get_tax_rate_ids();
|
874 |
-
if ( $order_taxes = $this->order->get_taxes() ) {
|
875 |
-
foreach ( $order_taxes as $key => $tax ) {
|
876 |
-
if ( WCX::is_wc_version_gte_3_0() ) {
|
877 |
-
$taxes[ $key ] = array(
|
878 |
-
'label' => $tax->get_label(),
|
879 |
-
'value' => $this->format_price( $tax->get_tax_total() + $tax->get_shipping_tax_total() ),
|
880 |
-
'rate_id' => $tax->get_rate_id(),
|
881 |
-
'tax_amount' => $tax->get_tax_total(),
|
882 |
-
'shipping_tax_amount' => $tax->get_shipping_tax_total(),
|
883 |
-
'rate' => isset( $tax_rate_ids[ $tax->get_rate_id() ] ) ? ( (float) $tax_rate_ids[$tax->get_rate_id()]['tax_rate'] ) . ' %': '',
|
884 |
-
);
|
885 |
-
} else {
|
886 |
-
$taxes[ $key ] = array(
|
887 |
-
'label' => isset( $tax[ 'label' ] ) ? $tax[ 'label' ] : $tax[ 'name' ],
|
888 |
-
'value' => $this->format_price( ( $tax[ 'tax_amount' ] + $tax[ 'shipping_tax_amount' ] ) ),
|
889 |
-
'rate_id' => $tax['rate_id'],
|
890 |
-
'tax_amount' => $tax['tax_amount'],
|
891 |
-
'shipping_tax_amount' => $tax['shipping_tax_amount'],
|
892 |
-
'rate' => isset( $tax_rate_ids[ $tax['rate_id'] ] ) ? ( (float) $tax_rate_ids[$tax['rate_id']]['tax_rate'] ) . ' %': '',
|
893 |
-
);
|
894 |
-
}
|
895 |
-
|
896 |
-
}
|
897 |
-
|
898 |
-
return apply_filters( 'wpo_wcpdf_order_taxes', $taxes, $this );
|
899 |
-
}
|
900 |
-
}
|
901 |
-
|
902 |
-
/**
|
903 |
-
* Return/show the order grand total
|
904 |
-
*/
|
905 |
-
public function get_order_grand_total( $tax = 'incl' ) {
|
906 |
-
if ( version_compare( WOOCOMMERCE_VERSION, '2.1' ) >= 0 ) {
|
907 |
-
// WC 2.1 or newer is used
|
908 |
-
$total_unformatted = $this->order->get_total();
|
909 |
-
} else {
|
910 |
-
// Backwards compatibility
|
911 |
-
$total_unformatted = $this->order->get_order_total();
|
912 |
-
}
|
913 |
-
|
914 |
-
if ($tax == 'excl' ) {
|
915 |
-
$total = $this->format_price( $total_unformatted - $this->order->get_total_tax() );
|
916 |
-
$label = __( 'Total ex. VAT', 'woocommerce-pdf-invoices-packing-slips' );
|
917 |
-
} else {
|
918 |
-
$total = $this->format_price( ( $total_unformatted ) );
|
919 |
-
$label = __( 'Total', 'woocommerce-pdf-invoices-packing-slips' );
|
920 |
-
}
|
921 |
-
|
922 |
-
$grand_total = array(
|
923 |
-
'label' => $label,
|
924 |
-
'value' => $total,
|
925 |
-
);
|
926 |
-
|
927 |
-
return apply_filters( 'wpo_wcpdf_order_grand_total', $grand_total, $tax, $this );
|
928 |
-
}
|
929 |
-
public function order_grand_total( $tax = 'incl' ) {
|
930 |
-
$grand_total = $this->get_order_grand_total( $tax );
|
931 |
-
echo $grand_total['value'];
|
932 |
-
}
|
933 |
-
|
934 |
-
|
935 |
-
/**
|
936 |
-
* Return/Show shipping notes
|
937 |
-
*/
|
938 |
-
public function get_shipping_notes() {
|
939 |
-
if ( $this->is_refund( $this->order ) ) {
|
940 |
-
// return reason for refund if order is a refund
|
941 |
-
if ( version_compare( WOOCOMMERCE_VERSION, '3.0', '>=' ) ) {
|
942 |
-
$shipping_notes = $this->order->get_reason();
|
943 |
-
} elseif ( method_exists($this->order, 'get_refund_reason') ) {
|
944 |
-
$shipping_notes = $this->order->get_refund_reason();
|
945 |
-
} else {
|
946 |
-
$shipping_notes = wpautop( wptexturize( WCX_Order::get_prop( $this->order, 'customer_note', 'view' ) ) );
|
947 |
-
}
|
948 |
-
} else {
|
949 |
-
$shipping_notes = wpautop( wptexturize( WCX_Order::get_prop( $this->order, 'customer_note', 'view' ) ) );
|
950 |
-
}
|
951 |
-
return apply_filters( 'wpo_wcpdf_shipping_notes', $shipping_notes, $this );
|
952 |
-
}
|
953 |
-
public function shipping_notes() {
|
954 |
-
echo $this->get_shipping_notes();
|
955 |
-
}
|
956 |
-
|
957 |
-
/**
|
958 |
-
* wrapper for wc_price, ensuring currency is always passed
|
959 |
-
*/
|
960 |
-
public function format_price( $price, $args = array() ) {
|
961 |
-
if ( function_exists( 'wc_price' ) ) { // WC 2.1+
|
962 |
-
$args['currency'] = WCX_Order::get_prop( $this->order, 'currency' );
|
963 |
-
$formatted_price = wc_price( $price, $args );
|
964 |
-
} else {
|
965 |
-
$formatted_price = woocommerce_price( $price );
|
966 |
-
}
|
967 |
-
|
968 |
-
return $formatted_price;
|
969 |
-
}
|
970 |
-
public function wc_price( $price, $args = array() ) {
|
971 |
-
return $this->format_price( $price, $args );
|
972 |
-
}
|
973 |
-
|
974 |
-
/**
|
975 |
-
* Gets price - formatted for display.
|
976 |
-
*
|
977 |
-
* @access public
|
978 |
-
* @param mixed $item
|
979 |
-
* @return string
|
980 |
-
*/
|
981 |
-
public function get_formatted_item_price ( $item, $type, $tax_display = '' ) {
|
982 |
-
if ( ! isset( $item['line_subtotal'] ) || ! isset( $item['line_subtotal_tax'] ) ) {
|
983 |
-
return;
|
984 |
-
}
|
985 |
-
|
986 |
-
$divide_by = ($type == 'single' && $item['qty'] != 0 )?abs($item['qty']):1; //divide by 1 if $type is not 'single' (thus 'total')
|
987 |
-
if ( $tax_display == 'excl' ) {
|
988 |
-
$item_price = $this->format_price( ($this->order->get_line_subtotal( $item )) / $divide_by );
|
989 |
-
} else {
|
990 |
-
$item_price = $this->format_price( ($this->order->get_line_subtotal( $item, true )) / $divide_by );
|
991 |
-
}
|
992 |
-
|
993 |
-
return $item_price;
|
994 |
-
}
|
995 |
-
|
996 |
-
public function get_invoice_number() {
|
997 |
-
// Call the woocommerce_invoice_number filter and let third-party plugins set a number.
|
998 |
-
// Default is null, so we can detect whether a plugin has set the invoice number
|
999 |
-
$third_party_invoice_number = apply_filters( 'woocommerce_invoice_number', null, $this->order_id );
|
1000 |
-
if ($third_party_invoice_number !== null) {
|
1001 |
-
return $third_party_invoice_number;
|
1002 |
-
}
|
1003 |
-
|
1004 |
-
if ( $invoice_number = $this->get_number('invoice') ) {
|
1005 |
-
return $formatted_invoice_number = $invoice_number->get_formatted();
|
1006 |
-
} else {
|
1007 |
-
return '';
|
1008 |
-
}
|
1009 |
-
}
|
1010 |
-
|
1011 |
-
public function invoice_number() {
|
1012 |
-
echo $this->get_invoice_number();
|
1013 |
-
}
|
1014 |
-
|
1015 |
-
public function get_invoice_date() {
|
1016 |
-
if ( $invoice_date = $this->get_date('invoice') ) {
|
1017 |
-
return $invoice_date->date_i18n( apply_filters( 'wpo_wcpdf_date_format', wc_date_format(), $this ) );
|
1018 |
-
} else {
|
1019 |
-
return '';
|
1020 |
-
}
|
1021 |
-
}
|
1022 |
-
|
1023 |
-
public function invoice_date() {
|
1024 |
-
echo $this->get_invoice_date();
|
1025 |
-
}
|
1026 |
-
|
1027 |
-
|
1028 |
-
}
|
1029 |
-
|
1030 |
endif; // class_exists
|
1 |
+
<?php
|
2 |
+
namespace WPO\WC\PDF_Invoices\Documents;
|
3 |
+
|
4 |
+
use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
|
5 |
+
use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
|
6 |
+
use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
|
7 |
+
|
8 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
9 |
+
exit; // Exit if accessed directly
|
10 |
+
}
|
11 |
+
|
12 |
+
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Documents\\Order_Document_Methods' ) ) :
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Abstract Order Methods
|
16 |
+
*
|
17 |
+
* Collection of methods to be used on orders within a Document
|
18 |
+
* Created as abstract rather than traits to support PHP versions older than 5.4
|
19 |
+
*
|
20 |
+
* @class \WPO\WC\PDF_Invoices\Documents\Order_Document_Methods
|
21 |
+
* @version 2.0
|
22 |
+
* @category Class
|
23 |
+
* @author Ewout Fernhout
|
24 |
+
*/
|
25 |
+
|
26 |
+
abstract class Order_Document_Methods extends Order_Document {
|
27 |
+
public function is_refund( $order ) {
|
28 |
+
if ( method_exists( $order, 'get_type') ) { // WC 3.0+
|
29 |
+
$is_refund = $order->get_type() == 'shop_order_refund';
|
30 |
+
} else {
|
31 |
+
$is_refund = get_post_type( WCX_Order::get_id( $order ) ) == 'shop_order_refund';
|
32 |
+
}
|
33 |
+
|
34 |
+
return $is_refund;
|
35 |
+
}
|
36 |
+
|
37 |
+
public function get_refund_parent_id( $order ) {
|
38 |
+
if ( method_exists( $order, 'get_parent_id') ) { // WC3.0+
|
39 |
+
$parent_order_id = $order->get_parent_id();
|
40 |
+
} else {
|
41 |
+
$parent_order_id = wp_get_post_parent_id( WCX_Order::get_id( $order ) );
|
42 |
+
}
|
43 |
+
|
44 |
+
return $parent_order_id;
|
45 |
+
}
|
46 |
+
|
47 |
+
|
48 |
+
public function get_refund_parent( $order ) {
|
49 |
+
// only try if this is actually a refund
|
50 |
+
if ( ! $this->is_refund( $order ) ) {
|
51 |
+
return $order;
|
52 |
+
}
|
53 |
+
|
54 |
+
$parent_order_id = $this->get_refund_parent_id( $order );
|
55 |
+
$order = WCX::get_order( $parent_order_id );
|
56 |
+
return $order;
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Check if billing address and shipping address are equal
|
61 |
+
*/
|
62 |
+
public function ships_to_different_address() {
|
63 |
+
// always prefer parent address for refunds
|
64 |
+
if ( $this->is_refund( $this->order ) ) {
|
65 |
+
$order = $this->get_refund_parent( $this->order );
|
66 |
+
} else {
|
67 |
+
$order = $this->order;
|
68 |
+
}
|
69 |
+
|
70 |
+
$address_comparison_fields = apply_filters( 'wpo_wcpdf_address_comparison_fields', array(
|
71 |
+
'first_name',
|
72 |
+
'last_name',
|
73 |
+
'company',
|
74 |
+
'address_1',
|
75 |
+
'address_2',
|
76 |
+
'city',
|
77 |
+
'state',
|
78 |
+
'postcode',
|
79 |
+
'country'
|
80 |
+
), $this );
|
81 |
+
|
82 |
+
foreach ($address_comparison_fields as $address_field) {
|
83 |
+
$billing_field = WCX_Order::get_prop( $order, "billing_{$address_field}", 'view');
|
84 |
+
$shipping_field = WCX_Order::get_prop( $order, "shipping_{$address_field}", 'view');
|
85 |
+
if ( $shipping_field != $billing_field ) {
|
86 |
+
// this address field is different -> ships to different address!
|
87 |
+
return true;
|
88 |
+
}
|
89 |
+
}
|
90 |
+
|
91 |
+
//if we got here, it means the addresses are equal -> doesn't ship to different address!
|
92 |
+
return apply_filters( 'wpo_wcpdf_ships_to_different_address', false, $order, $this );
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Return/Show billing address
|
97 |
+
*/
|
98 |
+
public function get_billing_address() {
|
99 |
+
// always prefer parent billing address for refunds
|
100 |
+
if ( $this->is_refund( $this->order ) ) {
|
101 |
+
// temporarily switch order to make all filters / order calls work correctly
|
102 |
+
$refund = $this->order;
|
103 |
+
$this->order = $this->get_refund_parent( $this->order );
|
104 |
+
$address = apply_filters( 'wpo_wcpdf_billing_address', $this->order->get_formatted_billing_address(), $this );
|
105 |
+
// switch back & unset
|
106 |
+
$this->order = $refund;
|
107 |
+
unset($refund);
|
108 |
+
} elseif ( $address = $this->order->get_formatted_billing_address() ) {
|
109 |
+
// regular shop_order
|
110 |
+
$address = apply_filters( 'wpo_wcpdf_billing_address', $address, $this );
|
111 |
+
} else {
|
112 |
+
// no address
|
113 |
+
$address = apply_filters( 'wpo_wcpdf_billing_address', __('N/A', 'woocommerce-pdf-invoices-packing-slips' ), $this );
|
114 |
+
}
|
115 |
+
|
116 |
+
return $address;
|
117 |
+
}
|
118 |
+
public function billing_address() {
|
119 |
+
echo $this->get_billing_address();
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* Return/Show billing email
|
124 |
+
*/
|
125 |
+
public function get_billing_email() {
|
126 |
+
$billing_email = WCX_Order::get_prop( $this->order, 'billing_email', 'view' );
|
127 |
+
|
128 |
+
if ( !$billing_email && $this->is_refund( $this->order ) ) {
|
129 |
+
// try parent
|
130 |
+
$parent_order = $this->get_refund_parent( $this->order );
|
131 |
+
$billing_email = WCX_Order::get_prop( $parent_order, 'billing_email', 'view' );
|
132 |
+
}
|
133 |
+
|
134 |
+
return apply_filters( 'wpo_wcpdf_billing_email', $billing_email, $this );
|
135 |
+
}
|
136 |
+
public function billing_email() {
|
137 |
+
echo $this->get_billing_email();
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Return/Show billing phone
|
142 |
+
*/
|
143 |
+
public function get_billing_phone() {
|
144 |
+
$billing_phone = WCX_Order::get_prop( $this->order, 'billing_phone', 'view' );
|
145 |
+
|
146 |
+
if ( !$billing_phone && $this->is_refund( $this->order ) ) {
|
147 |
+
// try parent
|
148 |
+
$parent_order = $this->get_refund_parent( $this->order );
|
149 |
+
$billing_phone = WCX_Order::get_prop( $parent_order, 'billing_phone', 'view' );
|
150 |
+
}
|
151 |
+
|
152 |
+
return apply_filters( 'wpo_wcpdf_billing_phone', $billing_phone, $this );
|
153 |
+
}
|
154 |
+
public function billing_phone() {
|
155 |
+
echo $this->get_billing_phone();
|
156 |
+
}
|
157 |
+
|
158 |
+
/**
|
159 |
+
* Return/Show shipping address
|
160 |
+
*/
|
161 |
+
public function get_shipping_address() {
|
162 |
+
// always prefer parent shipping address for refunds
|
163 |
+
if ( $this->is_refund( $this->order ) ) {
|
164 |
+
// temporarily switch order to make all filters / order calls work correctly
|
165 |
+
$refund = $this->order;
|
166 |
+
$this->order = $this->get_refund_parent( $this->order );
|
167 |
+
$address = apply_filters( 'wpo_wcpdf_shipping_address', $this->order->get_formatted_shipping_address(), $this );
|
168 |
+
// switch back & unset
|
169 |
+
$this->order = $refund;
|
170 |
+
unset($refund);
|
171 |
+
} elseif ( $address = $this->order->get_formatted_shipping_address() ) {
|
172 |
+
// regular shop_order
|
173 |
+
$address = apply_filters( 'wpo_wcpdf_shipping_address', $address, $this );
|
174 |
+
} else {
|
175 |
+
// no address
|
176 |
+
$address = apply_filters( 'wpo_wcpdf_shipping_address', __('N/A', 'woocommerce-pdf-invoices-packing-slips' ), $this );
|
177 |
+
}
|
178 |
+
|
179 |
+
return $address;
|
180 |
+
}
|
181 |
+
public function shipping_address() {
|
182 |
+
echo $this->get_shipping_address();
|
183 |
+
}
|
184 |
+
|
185 |
+
/**
|
186 |
+
* Return/Show a custom field
|
187 |
+
*/
|
188 |
+
public function get_custom_field( $field_name ) {
|
189 |
+
$custom_field = WCX_Order::get_meta( $this->order, $field_name, true );
|
190 |
+
// if not found, try prefixed with underscore
|
191 |
+
if ( !$custom_field && substr( $field_name, 0, 1 ) !== '_' ) {
|
192 |
+
$custom_field = WCX_Order::get_meta( $this->order, "_{$field_name}", true );
|
193 |
+
}
|
194 |
+
|
195 |
+
// WC3.0 fallback to properties
|
196 |
+
$property = str_replace('-', '_', sanitize_title( ltrim($field_name, '_') ) );
|
197 |
+
if ( !$custom_field && is_callable( array( $this->order, "get_{$property}" ) ) ) {
|
198 |
+
$custom_field = $this->order->{"get_{$property}"}( 'view' );
|
199 |
+
}
|
200 |
+
|
201 |
+
// fallback to parent for refunds
|
202 |
+
if ( !$custom_field && $this->is_refund( $this->order ) ) {
|
203 |
+
$parent_order = $this->get_refund_parent( $this->order );
|
204 |
+
$custom_field = WCX_Order::get_meta( $parent_order, $field_name, true );
|
205 |
+
|
206 |
+
// WC3.0 fallback to properties
|
207 |
+
if ( !$custom_field && is_callable( array( $parent_order, "get_{$property}" ) ) ) {
|
208 |
+
$custom_field = $parent_order->{"get_{$property}"}( 'view' );
|
209 |
+
}
|
210 |
+
}
|
211 |
+
|
212 |
+
return apply_filters( 'wpo_wcpdf_billing_custom_field', $custom_field, $this );
|
213 |
+
}
|
214 |
+
public function custom_field( $field_name, $field_label = '', $display_empty = false ) {
|
215 |
+
$custom_field = $this->get_custom_field( $field_name );
|
216 |
+
if (!empty($field_label)){
|
217 |
+
// add a a trailing space to the label
|
218 |
+
$field_label .= ' ';
|
219 |
+
}
|
220 |
+
|
221 |
+
if (!empty($custom_field) || $display_empty) {
|
222 |
+
echo $field_label . nl2br ($custom_field);
|
223 |
+
}
|
224 |
+
}
|
225 |
+
|
226 |
+
/**
|
227 |
+
* Return/show product attribute
|
228 |
+
*/
|
229 |
+
public function get_product_attribute( $attribute_name, $product ) {
|
230 |
+
// first, check the text attributes
|
231 |
+
$attributes = $product->get_attributes();
|
232 |
+
$attribute_key = @wc_attribute_taxonomy_name( $attribute_name );
|
233 |
+
if (array_key_exists( sanitize_title( $attribute_name ), $attributes) ) {
|
234 |
+
$attribute = $product->get_attribute ( $attribute_name );
|
235 |
+
} elseif (array_key_exists( sanitize_title( $attribute_key ), $attributes) ) {
|
236 |
+
$attribute = $product->get_attribute ( $attribute_key );
|
237 |
+
}
|
238 |
+
|
239 |
+
if (empty($attribute)) {
|
240 |
+
// not a text attribute, try attribute taxonomy
|
241 |
+
$attribute_key = @wc_attribute_taxonomy_name( $attribute_name );
|
242 |
+
$product_id = WCX_Product::get_prop($product, 'id');
|
243 |
+
$product_terms = @wc_get_product_terms( $product_id, $attribute_key, array( 'fields' => 'names' ) );
|
244 |
+
// check if not empty, then display
|
245 |
+
if ( !empty($product_terms) ) {
|
246 |
+
$attribute = array_shift( $product_terms );
|
247 |
+
}
|
248 |
+
}
|
249 |
+
|
250 |
+
// WC3.0+ fallback parent product for variations
|
251 |
+
if ( empty($attribute) && version_compare( WOOCOMMERCE_VERSION, '3.0', '>=' ) && $product->is_type( 'variation' ) ) {
|
252 |
+
$product = wc_get_product( $product->get_parent_id() );
|
253 |
+
$attribute = $this->get_product_attribute( $attribute_name, $product );
|
254 |
+
}
|
255 |
+
|
256 |
+
return isset($attribute) ? $attribute : false;
|
257 |
+
}
|
258 |
+
public function product_attribute( $attribute_name, $product ) {
|
259 |
+
echo $this->get_product_attribute( $attribute_name, $product );
|
260 |
+
}
|
261 |
+
|
262 |
+
/**
|
263 |
+
* Return/Show order notes
|
264 |
+
* could use $order->get_customer_order_notes(), but that filters out private notes already
|
265 |
+
*/
|
266 |
+
public function get_order_notes( $filter = 'customer' ) {
|
267 |
+
if ( $this->is_refund( $this->order ) ) {
|
268 |
+
$post_id = $this->get_refund_parent_id( $this->order );
|
269 |
+
} else {
|
270 |
+
$post_id = $this->order_id;
|
271 |
+
}
|
272 |
+
|
273 |
+
if ( empty( $post_id ) ) {
|
274 |
+
return; // prevent order notes from all orders showing when document is not loaded properly
|
275 |
+
}
|
276 |
+
|
277 |
+
$args = array(
|
278 |
+
'post_id' => $post_id,
|
279 |
+
'approve' => 'approve',
|
280 |
+
'type' => 'order_note'
|
281 |
+
);
|
282 |
+
|
283 |
+
remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
|
284 |
+
|
285 |
+
$notes = get_comments( $args );
|
286 |
+
|
287 |
+
add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
|
288 |
+
|
289 |
+
if ( $notes ) {
|
290 |
+
foreach( $notes as $key => $note ) {
|
291 |
+
if ( $filter == 'customer' && !get_comment_meta( $note->comment_ID, 'is_customer_note', true ) ) {
|
292 |
+
unset($notes[$key]);
|
293 |
+
}
|
294 |
+
if ( $filter == 'private' && get_comment_meta( $note->comment_ID, 'is_customer_note', true ) ) {
|
295 |
+
unset($notes[$key]);
|
296 |
+
}
|
297 |
+
}
|
298 |
+
return $notes;
|
299 |
+
}
|
300 |
+
}
|
301 |
+
public function order_notes( $filter = 'customer' ) {
|
302 |
+
$notes = $this->get_order_notes( $filter );
|
303 |
+
if ( $notes ) {
|
304 |
+
foreach( $notes as $note ) {
|
305 |
+
?>
|
306 |
+
<div class="note_content">
|
307 |
+
<?php echo wpautop( wptexturize( wp_kses_post( $note->comment_content ) ) ); ?>
|
308 |
+
</div>
|
309 |
+
<?php
|
310 |
+
}
|
311 |
+
}
|
312 |
+
}
|
313 |
+
|
314 |
+
/**
|
315 |
+
* Return/Show the current date
|
316 |
+
*/
|
317 |
+
public function get_current_date() {
|
318 |
+
return apply_filters( 'wpo_wcpdf_date', date_i18n( get_option( 'date_format' ) ) );
|
319 |
+
}
|
320 |
+
public function current_date() {
|
321 |
+
echo $this->get_current_date();
|
322 |
+
}
|
323 |
+
|
324 |
+
/**
|
325 |
+
* Return/Show payment method
|
326 |
+
*/
|
327 |
+
public function get_payment_method() {
|
328 |
+
$payment_method_label = __( 'Payment method', 'woocommerce-pdf-invoices-packing-slips' );
|
329 |
+
|
330 |
+
if ( $this->is_refund( $this->order ) ) {
|
331 |
+
$parent_order = $this->get_refund_parent( $this->order );
|
332 |
+
$payment_method_title = WCX_Order::get_prop( $parent_order, 'payment_method_title', 'view' );
|
333 |
+
} else {
|
334 |
+
$payment_method_title = WCX_Order::get_prop( $this->order, 'payment_method_title', 'view' );
|
335 |
+
}
|
336 |
+
|
337 |
+
$payment_method = __( $payment_method_title, 'woocommerce' );
|
338 |
+
|
339 |
+
return apply_filters( 'wpo_wcpdf_payment_method', $payment_method, $this );
|
340 |
+
}
|
341 |
+
public function payment_method() {
|
342 |
+
echo $this->get_payment_method();
|
343 |
+
}
|
344 |
+
|
345 |
+
/**
|
346 |
+
* Return/Show shipping method
|
347 |
+
*/
|
348 |
+
public function get_shipping_method() {
|
349 |
+
$shipping_method_label = __( 'Shipping method', 'woocommerce-pdf-invoices-packing-slips' );
|
350 |
+
$shipping_method = __( $this->order->get_shipping_method(), 'woocommerce' );
|
351 |
+
return apply_filters( 'wpo_wcpdf_shipping_method', $shipping_method, $this );
|
352 |
+
}
|
353 |
+
public function shipping_method() {
|
354 |
+
echo $this->get_shipping_method();
|
355 |
+
}
|
356 |
+
|
357 |
+
/**
|
358 |
+
* Return/Show order number
|
359 |
+
*/
|
360 |
+
public function get_order_number() {
|
361 |
+
// try parent first
|
362 |
+
if ( $this->is_refund( $this->order ) ) {
|
363 |
+
$parent_order = $this->get_refund_parent( $this->order );
|
364 |
+
$order_number = $parent_order->get_order_number();
|
365 |
+
} else {
|
366 |
+
$order_number = $this->order->get_order_number();
|
367 |
+
}
|
368 |
+
|
369 |
+
// Trim the hash to have a clean number but still
|
370 |
+
// support any filters that were applied before.
|
371 |
+
$order_number = ltrim($order_number, '#');
|
372 |
+
return apply_filters( 'wpo_wcpdf_order_number', $order_number, $this );
|
373 |
+
}
|
374 |
+
public function order_number() {
|
375 |
+
echo $this->get_order_number();
|
376 |
+
}
|
377 |
+
|
378 |
+
/**
|
379 |
+
* Return/Show the order date
|
380 |
+
*/
|
381 |
+
public function get_order_date() {
|
382 |
+
if ( $this->is_refund( $this->order ) ) {
|
383 |
+
$parent_order = $this->get_refund_parent( $this->order );
|
384 |
+
$order_date = WCX_Order::get_prop( $parent_order, 'date_created' );
|
385 |
+
} else {
|
386 |
+
$order_date = WCX_Order::get_prop( $this->order, 'date_created' );
|
387 |
+
}
|
388 |
+
|
389 |
+
$date = $order_date->date_i18n( get_option( 'date_format' ) );
|
390 |
+
$mysql_date = $order_date->date( "Y-m-d H:i:s" );
|
391 |
+
return apply_filters( 'wpo_wcpdf_order_date', $date, $mysql_date, $this );
|
392 |
+
}
|
393 |
+
public function order_date() {
|
394 |
+
echo $this->get_order_date();
|
395 |
+
}
|
396 |
+
|
397 |
+
/**
|
398 |
+
* Return the order items
|
399 |
+
*/
|
400 |
+
public function get_order_items() {
|
401 |
+
$items = $this->order->get_items();
|
402 |
+
$data_list = array();
|
403 |
+
|
404 |
+
if( sizeof( $items ) > 0 ) {
|
405 |
+
foreach ( $items as $item_id => $item ) {
|
406 |
+
// Array with data for the pdf template
|
407 |
+
$data = array();
|
408 |
+
|
409 |
+
// Set the item_id
|
410 |
+
$data['item_id'] = $item_id;
|
411 |
+
|
412 |
+
// Set the id
|
413 |
+
$data['product_id'] = $item['product_id'];
|
414 |
+
$data['variation_id'] = $item['variation_id'];
|
415 |
+
|
416 |
+
// Set item name
|
417 |
+
$data['name'] = $item['name'];
|
418 |
+
|
419 |
+
// Set item quantity
|
420 |
+
$data['quantity'] = $item['qty'];
|
421 |
+
|
422 |
+
// Set the line total (=after discount)
|
423 |
+
$data['line_total'] = $this->format_price( $item['line_total'] );
|
424 |
+
$data['single_line_total'] = $this->format_price( $item['line_total'] / max( 1, abs( $item['qty'] ) ) );
|
425 |
+
$data['line_tax'] = $this->format_price( $item['line_tax'] );
|
426 |
+
$data['single_line_tax'] = $this->format_price( $item['line_tax'] / max( 1, abs( $item['qty'] ) ) );
|
427 |
+
|
428 |
+
$line_tax_data = maybe_unserialize( isset( $item['line_tax_data'] ) ? $item['line_tax_data'] : '' );
|
429 |
+
$data['tax_rates'] = $this->get_tax_rate( $item['tax_class'], $item['line_total'], $item['line_tax'], $line_tax_data );
|
430 |
+
|
431 |
+
// Set the line subtotal (=before discount)
|
432 |
+
$data['line_subtotal'] = $this->format_price( $item['line_subtotal'] );
|
433 |
+
$data['line_subtotal_tax'] = $this->format_price( $item['line_subtotal_tax'] );
|
434 |
+
$data['ex_price'] = $this->get_formatted_item_price( $item, 'total', 'excl' );
|
435 |
+
$data['price'] = $this->get_formatted_item_price( $item, 'total' );
|
436 |
+
$data['order_price'] = $this->order->get_formatted_line_subtotal( $item ); // formatted according to WC settings
|
437 |
+
|
438 |
+
// Calculate the single price with the same rules as the formatted line subtotal (!)
|
439 |
+
// = before discount
|
440 |
+
$data['ex_single_price'] = $this->get_formatted_item_price( $item, 'single', 'excl' );
|
441 |
+
$data['single_price'] = $this->get_formatted_item_price( $item, 'single' );
|
442 |
+
|
443 |
+
// Pass complete item array
|
444 |
+
$data['item'] = $item;
|
445 |
+
|
446 |
+
// Get the product to add more info
|
447 |
+
$product = $this->order->get_product_from_item( $item );
|
448 |
+
|
449 |
+
// Checking fo existance, thanks to MDesigner0
|
450 |
+
if( !empty( $product ) ) {
|
451 |
+
// Thumbnail (full img tag)
|
452 |
+
$data['thumbnail'] = $this->get_thumbnail( $product );
|
453 |
+
|
454 |
+
// Set item SKU
|
455 |
+
$data['sku'] = $product->get_sku();
|
456 |
+
|
457 |
+
// Set item weight
|
458 |
+
$data['weight'] = $product->get_weight();
|
459 |
+
|
460 |
+
// Set item dimensions
|
461 |
+
$data['dimensions'] = WCX_Product::get_dimensions( $product );
|
462 |
+
|
463 |
+
// Pass complete product object
|
464 |
+
$data['product'] = $product;
|
465 |
+
|
466 |
+
} else {
|
467 |
+
$data['product'] = null;
|
468 |
+
}
|
469 |
+
|
470 |
+
// Set item meta
|
471 |
+
if (function_exists('wc_display_item_meta')) { // WC3.0+
|
472 |
+
$data['meta'] = wc_display_item_meta( $item, array(
|
473 |
+
'echo' => false,
|
474 |
+
) );
|
475 |
+
} else {
|
476 |
+
if ( version_compare( WOOCOMMERCE_VERSION, '2.4', '<' ) ) {
|
477 |
+
$meta = new \WC_Order_Item_Meta( $item['item_meta'], $product );
|
478 |
+
} else { // pass complete item for WC2.4+
|
479 |
+
$meta = new \WC_Order_Item_Meta( $item, $product );
|
480 |
+
}
|
481 |
+
$data['meta'] = $meta->display( false, true );
|
482 |
+
}
|
483 |
+
|
484 |
+
$data_list[$item_id] = apply_filters( 'wpo_wcpdf_order_item_data', $data, $this->order, $this->get_type() );
|
485 |
+
}
|
486 |
+
}
|
487 |
+
|
488 |
+
return apply_filters( 'wpo_wcpdf_order_items_data', $data_list, $this->order, $this->get_type() );
|
489 |
+
}
|
490 |
+
|
491 |
+
/**
|
492 |
+
* Get the tax rates/percentages for a given tax class
|
493 |
+
* @param string $tax_class tax class slug
|
494 |
+
* @return string $tax_rates imploded list of tax rates
|
495 |
+
*/
|
496 |
+
public function get_tax_rate( $tax_class, $line_total, $line_tax, $line_tax_data = '' ) {
|
497 |
+
// first try the easy wc2.2+ way, using line_tax_data
|
498 |
+
if ( !empty( $line_tax_data ) && isset($line_tax_data['total']) ) {
|
499 |
+
$tax_rates = array();
|
500 |
+
|
501 |
+
$line_taxes = $line_tax_data['subtotal'];
|
502 |
+
foreach ( $line_taxes as $tax_id => $tax ) {
|
503 |
+
if ( isset($tax) && $tax !== '' ) {
|
504 |
+
$tax_rates[] = $this->get_tax_rate_by_id( $tax_id ) . ' %';
|
505 |
+
}
|
506 |
+
}
|
507 |
+
|
508 |
+
$tax_rates = implode(' ,', $tax_rates );
|
509 |
+
return $tax_rates;
|
510 |
+
}
|
511 |
+
|
512 |
+
if ( $line_tax == 0 ) {
|
513 |
+
return '-'; // no need to determine tax rate...
|
514 |
+
}
|
515 |
+
|
516 |
+
if ( version_compare( WOOCOMMERCE_VERSION, '2.1' ) >= 0 && !apply_filters( 'wpo_wcpdf_calculate_tax_rate', false ) ) {
|
517 |
+
// WC 2.1 or newer is used
|
518 |
+
$tax = new \WC_Tax();
|
519 |
+
$taxes = $tax->get_rates( $tax_class );
|
520 |
+
|
521 |
+
$tax_rates = array();
|
522 |
+
|
523 |
+
foreach ($taxes as $tax) {
|
524 |
+
$tax_rates[$tax['label']] = round( $tax['rate'], 2 ).' %';
|
525 |
+
}
|
526 |
+
|
527 |
+
if (empty($tax_rates)) {
|
528 |
+
// one last try: manually calculate
|
529 |
+
if ( $line_total != 0) {
|
530 |
+
$tax_rates[] = round( ($line_tax / $line_total)*100, 1 ).' %';
|
531 |
+
} else {
|
532 |
+
$tax_rates[] = '-';
|
533 |
+
}
|
534 |
+
}
|
535 |
+
|
536 |
+
$tax_rates = implode(' ,', $tax_rates );
|
537 |
+
} else {
|
538 |
+
// Backwards compatibility/fallback: calculate tax from line items
|
539 |
+
if ( $line_total != 0) {
|
540 |
+
$tax_rates = round( ($line_tax / $line_total)*100, 1 ).' %';
|
541 |
+
} else {
|
542 |
+
$tax_rates = '-';
|
543 |
+
}
|
544 |
+
}
|
545 |
+
|
546 |
+
return $tax_rates;
|
547 |
+
}
|
548 |
+
|
549 |
+
/**
|
550 |
+
* Returns the percentage rate (float) for a given tax rate ID.
|
551 |
+
* @param int $rate_id woocommerce tax rate id
|
552 |
+
* @return float $rate percentage rate
|
553 |
+
*/
|
554 |
+
public function get_tax_rate_by_id( $rate_id ) {
|
555 |
+
global $wpdb;
|
556 |
+
$rate = $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %d;", $rate_id ) );
|
557 |
+
return (float) $rate;
|
558 |
+
}
|
559 |
+
|
560 |
+
/**
|
561 |
+
* Returns a an array with rate_id => tax rate data (array) of all tax rates in woocommerce
|
562 |
+
* @return array $tax_rate_ids keyed by id
|
563 |
+
*/
|
564 |
+
public function get_tax_rate_ids() {
|
565 |
+
global $wpdb;
|
566 |
+
$rates = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}woocommerce_tax_rates" );
|
567 |
+
|
568 |
+
$tax_rate_ids = array();
|
569 |
+
foreach ($rates as $rate) {
|
570 |
+
// var_dump($rate->tax_rate_id);
|
571 |
+
// die($rate);
|
572 |
+
$rate_id = $rate->tax_rate_id;
|
573 |
+
unset($rate->tax_rate_id);
|
574 |
+
$tax_rate_ids[$rate_id] = (array) $rate;
|
575 |
+
}
|
576 |
+
|
577 |
+
return $tax_rate_ids;
|
578 |
+
}
|
579 |
+
|
580 |
+
/**
|
581 |
+
* Returns the main product image ID
|
582 |
+
* Adapted from the WC_Product class
|
583 |
+
* (does not support thumbnail sizes)
|
584 |
+
*
|
585 |
+
* @access public
|
586 |
+
* @return string
|
587 |
+
*/
|
588 |
+
public function get_thumbnail_id ( $product ) {
|
589 |
+
global $woocommerce;
|
590 |
+
|
591 |
+
$product_id = WCX_Product::get_id( $product );
|
592 |
+
|
593 |
+
if ( has_post_thumbnail( $product_id ) ) {
|
594 |
+
$thumbnail_id = get_post_thumbnail_id ( $product_id );
|
595 |
+
} elseif ( ( $parent_id = wp_get_post_parent_id( $product_id ) ) && has_post_thumbnail( $parent_id ) ) {
|
596 |
+
$thumbnail_id = get_post_thumbnail_id ( $parent_id );
|
597 |
+
} else {
|
598 |
+
$thumbnail_id = false;
|
599 |
+
}
|
600 |
+
|
601 |
+
return $thumbnail_id;
|
602 |
+
}
|
603 |
+
|
604 |
+
/**
|
605 |
+
* Returns the thumbnail image tag
|
606 |
+
*
|
607 |
+
* uses the internal WooCommerce/WP functions and extracts the image url or path
|
608 |
+
* rather than the thumbnail ID, to simplify the code and make it possible to
|
609 |
+
* filter for different thumbnail sizes
|
610 |
+
*
|
611 |
+
* @access public
|
612 |
+
* @return string
|
613 |
+
*/
|
614 |
+
public function get_thumbnail ( $product ) {
|
615 |
+
// Get default WooCommerce img tag (url/http)
|
616 |
+
$size = apply_filters( 'wpo_wcpdf_thumbnail_size', 'shop_thumbnail' );
|
617 |
+
$thumbnail_img_tag_url = $product->get_image( $size, array( 'title' => '' ) );
|
618 |
+
|
619 |
+
// Extract the url from img
|
620 |
+
preg_match('/<img(.*)src(.*)=(.*)"(.*)"/U', $thumbnail_img_tag_url, $thumbnail_url );
|
621 |
+
$thumbnail_url = array_pop($thumbnail_url);
|
622 |
+
// remove http/https from image tag url to avoid mixed origin conflicts
|
623 |
+
$contextless_thumbnail_url = ltrim( str_replace(array('http://','https://'), '', $thumbnail_url ), '/' );
|
624 |
+
|
625 |
+
// convert url to path
|
626 |
+
if ( defined('WP_CONTENT_DIR') && strpos( WP_CONTENT_DIR, ABSPATH ) !== false ) {
|
627 |
+
$forwardslash_basepath = str_replace('\\','/', ABSPATH);
|
628 |
+
$contextless_site_url = str_replace(array('http://','https://'), '', trailingslashit(get_site_url()));
|
629 |
+
} else {
|
630 |
+
// bedrock e.a
|
631 |
+
$forwardslash_basepath = str_replace('\\','/', WP_CONTENT_DIR);
|
632 |
+
$contextless_site_url = str_replace(array('http://','https://'), '', trailingslashit(WP_CONTENT_URL));
|
633 |
+
}
|
634 |
+
$thumbnail_path = str_replace( $contextless_site_url, trailingslashit( $forwardslash_basepath ), $contextless_thumbnail_url);
|
635 |
+
|
636 |
+
// fallback if thumbnail file doesn't exist
|
637 |
+
if (apply_filters('wpo_wcpdf_use_path', true) && !file_exists($thumbnail_path)) {
|
638 |
+
if ($thumbnail_id = $this->get_thumbnail_id( $product ) ) {
|
639 |
+
$thumbnail_path = get_attached_file( $thumbnail_id );
|
640 |
+
}
|
641 |
+
}
|
642 |
+
|
643 |
+
// Thumbnail (full img tag)
|
644 |
+
if ( apply_filters('wpo_wcpdf_use_path', true) && file_exists($thumbnail_path) ) {
|
645 |
+
// load img with server path by default
|
646 |
+
$thumbnail = sprintf('<img width="90" height="90" src="%s" class="attachment-shop_thumbnail wp-post-image">', $thumbnail_path );
|
647 |
+
} elseif ( apply_filters('wpo_wcpdf_use_path', true) && !file_exists($thumbnail_path) ) {
|
648 |
+
// should use paths but file not found, replace // with http(s):// for dompdf compatibility
|
649 |
+
if ( substr( $thumbnail_url, 0, 2 ) === "//" ) {
|
650 |
+
$prefix = is_ssl() ? 'https://' : 'http://';
|
651 |
+
$https_thumbnail_url = $prefix . ltrim( $thumbnail_url, '/' );
|
652 |
+
}
|
653 |
+
$thumbnail_img_tag_url = str_replace($thumbnail_url, $https_thumbnail_url, $thumbnail_img_tag_url);
|
654 |
+
$thumbnail = $thumbnail_img_tag_url;
|
655 |
+
} else {
|
656 |
+
// load img with http url when filtered
|
657 |
+
$thumbnail = $thumbnail_img_tag_url;
|
658 |
+
}
|
659 |
+
|
660 |
+
// die($thumbnail);
|
661 |
+
return $thumbnail;
|
662 |
+
}
|
663 |
+
|
664 |
+
/**
|
665 |
+
* Return the order totals listing
|
666 |
+
*/
|
667 |
+
public function get_woocommerce_totals() {
|
668 |
+
// get totals and remove the semicolon
|
669 |
+
$totals = apply_filters( 'wpo_wcpdf_raw_order_totals', $this->order->get_order_item_totals(), $this->order );
|
670 |
+
|
671 |
+
// remove the colon for every label
|
672 |
+
foreach ( $totals as $key => $total ) {
|
673 |
+
$label = $total['label'];
|
674 |
+
$colon = strrpos( $label, ':' );
|
675 |
+
if( $colon !== false ) {
|
676 |
+
$label = substr_replace( $label, '', $colon, 1 );
|
677 |
+
}
|
678 |
+
$totals[$key]['label'] = $label;
|
679 |
+
}
|
680 |
+
|
681 |
+
// WC2.4 fix order_total for refunded orders
|
682 |
+
// not if this is the actual refund!
|
683 |
+
if ( ! $this->is_refund( $this->order ) ) {
|
684 |
+
$total_refunded = method_exists($this->order, 'get_total_refunded') ? $this->order->get_total_refunded() : 0;
|
685 |
+
if ( version_compare( WOOCOMMERCE_VERSION, '2.4', '>=' ) && isset($totals['order_total']) && $total_refunded ) {
|
686 |
+
if ( version_compare( WOOCOMMERCE_VERSION, '3.0', '>=' ) ) {
|
687 |
+
$tax_display = get_option( 'woocommerce_tax_display_cart' );
|
688 |
+
} else {
|
689 |
+
$tax_display = WCX_Order::get_prop( $this->order, 'tax_display_cart' );
|
690 |
+
}
|
691 |
+
|
692 |
+
$totals['order_total']['value'] = wc_price( $this->order->get_total(), array( 'currency' => WCX_Order::get_prop( $this->order, 'currency' ) ) );
|
693 |
+
$order_total = $this->order->get_total();
|
694 |
+
$tax_string = '';
|
695 |
+
|
696 |
+
// Tax for inclusive prices
|
697 |
+
if ( wc_tax_enabled() && 'incl' == $tax_display ) {
|
698 |
+
$tax_string_array = array();
|
699 |
+
if ( 'itemized' == get_option( 'woocommerce_tax_total_display' ) ) {
|
700 |
+
foreach ( $this->order->get_tax_totals() as $code => $tax ) {
|
701 |
+
$tax_amount = $tax->formatted_amount;
|
702 |
+
$tax_string_array[] = sprintf( '%s %s', $tax_amount, $tax->label );
|
703 |
+
}
|
704 |
+
} else {
|
705 |
+
$tax_string_array[] = sprintf( '%s %s', wc_price( $this->order->get_total_tax() - $this->order->get_total_tax_refunded(), array( 'currency' => WCX_Order::get_prop( $this->order, 'currency' ) ) ), WC()->countries->tax_or_vat() );
|
706 |
+
}
|
707 |
+
if ( ! empty( $tax_string_array ) ) {
|
708 |
+
if ( version_compare( WOOCOMMERCE_VERSION, '2.6', '>=' ) ) {
|
709 |
+
$tax_string = ' ' . sprintf( __( '(includes %s)', 'woocommerce' ), implode( ', ', $tax_string_array ) );
|
710 |
+
} else {
|
711 |
+
// use old capitalized string
|
712 |
+
$tax_string = ' ' . sprintf( __( '(Includes %s)', 'woocommerce' ), implode( ', ', $tax_string_array ) );
|
713 |
+
}
|
714 |
+
}
|
715 |
+
}
|
716 |
+
|
717 |
+
$totals['order_total']['value'] .= $tax_string;
|
718 |
+
}
|
719 |
+
|
720 |
+
// remove refund lines (shouldn't be in invoice)
|
721 |
+
foreach ( $totals as $key => $total ) {
|
722 |
+
if ( strpos($key, 'refund_') !== false ) {
|
723 |
+
unset( $totals[$key] );
|
724 |
+
}
|
725 |
+
}
|
726 |
+
|
727 |
+
}
|
728 |
+
|
729 |
+
return apply_filters( 'wpo_wcpdf_woocommerce_totals', $totals, $this->order, $this->get_type() );
|
730 |
+
}
|
731 |
+
|
732 |
+
/**
|
733 |
+
* Return/show the order subtotal
|
734 |
+
*/
|
735 |
+
public function get_order_subtotal( $tax = 'excl', $discount = 'incl' ) { // set $tax to 'incl' to include tax, same for $discount
|
736 |
+
//$compound = ($discount == 'incl')?true:false;
|
737 |
+
$subtotal = $this->order->get_subtotal_to_display( false, $tax );
|
738 |
+
|
739 |
+
$subtotal = ($pos = strpos($subtotal, ' <small')) ? substr($subtotal, 0, $pos) : $subtotal; //removing the 'excluding tax' text
|
740 |
+
|
741 |
+
$subtotal = array (
|
742 |
+
'label' => __('Subtotal', 'woocommerce-pdf-invoices-packing-slips' ),
|
743 |
+
'value' => $subtotal,
|
744 |
+
);
|
745 |
+
|
746 |
+
return apply_filters( 'wpo_wcpdf_order_subtotal', $subtotal, $tax, $discount, $this );
|
747 |
+
}
|
748 |
+
public function order_subtotal( $tax = 'excl', $discount = 'incl' ) {
|
749 |
+
$subtotal = $this->get_order_subtotal( $tax, $discount );
|
750 |
+
echo $subtotal['value'];
|
751 |
+
}
|
752 |
+
|
753 |
+
/**
|
754 |
+
* Return/show the order shipping costs
|
755 |
+
*/
|
756 |
+
public function get_order_shipping( $tax = 'excl' ) { // set $tax to 'incl' to include tax
|
757 |
+
$shipping_cost = WCX_Order::get_prop( $this->order, 'shipping_total', 'view' );
|
758 |
+
$shipping_tax = WCX_Order::get_prop( $this->order, 'shipping_tax', 'view' );
|
759 |
+
|
760 |
+
if ($tax == 'excl' ) {
|
761 |
+
$formatted_shipping_cost = $this->format_price( $shipping_cost );
|
762 |
+
} else {
|
763 |
+
$formatted_shipping_cost = $this->format_price( $shipping_cost + $shipping_tax );
|
764 |
+
}
|
765 |
+
|
766 |
+
$shipping = array (
|
767 |
+
'label' => __('Shipping', 'woocommerce-pdf-invoices-packing-slips' ),
|
768 |
+
'value' => $formatted_shipping_cost,
|
769 |
+
'tax' => $this->format_price( $shipping_tax ),
|
770 |
+
);
|
771 |
+
return apply_filters( 'wpo_wcpdf_order_shipping', $shipping, $tax, $this );
|
772 |
+
}
|
773 |
+
public function order_shipping( $tax = 'excl' ) {
|
774 |
+
$shipping = $this->get_order_shipping( $tax );
|
775 |
+
echo $shipping['value'];
|
776 |
+
}
|
777 |
+
|
778 |
+
/**
|
779 |
+
* Return/show the total discount
|
780 |
+
*/
|
781 |
+
public function get_order_discount( $type = 'total', $tax = 'incl' ) {
|
782 |
+
if ( $tax == 'incl' ) {
|
783 |
+
switch ($type) {
|
784 |
+
case 'cart':
|
785 |
+
// Cart Discount - pre-tax discounts. (deprecated in WC2.3)
|
786 |
+
$discount_value = $this->order->get_cart_discount();
|
787 |
+
break;
|
788 |
+
case 'order':
|
789 |
+
// Order Discount - post-tax discounts. (deprecated in WC2.3)
|
790 |
+
$discount_value = $this->order->get_order_discount();
|
791 |
+
break;
|
792 |
+
case 'total':
|
793 |
+
// Total Discount
|
794 |
+
if ( version_compare( WOOCOMMERCE_VERSION, '2.3' ) >= 0 ) {
|
795 |
+
$discount_value = $this->order->get_total_discount( false ); // $ex_tax = false
|
796 |
+
} else {
|
797 |
+
// WC2.2 and older: recalculate to include tax
|
798 |
+
$discount_value = 0;
|
799 |
+
$items = $this->order->get_items();;
|
800 |
+
if( sizeof( $items ) > 0 ) {
|
801 |
+
foreach( $items as $item ) {
|
802 |
+
$discount_value += ($item['line_subtotal'] + $item['line_subtotal_tax']) - ($item['line_total'] + $item['line_tax']);
|
803 |
+
}
|
804 |
+
}
|
805 |
+
}
|
806 |
+
|
807 |
+
break;
|
808 |
+
default:
|
809 |
+
// Total Discount - Cart & Order Discounts combined
|
810 |
+
$discount_value = $this->order->get_total_discount();
|
811 |
+
break;
|
812 |
+
}
|
813 |
+
} else { // calculate discount excluding tax
|
814 |
+
if ( version_compare( WOOCOMMERCE_VERSION, '2.3' ) >= 0 ) {
|
815 |
+
$discount_value = $this->order->get_total_discount( true ); // $ex_tax = true
|
816 |
+
} else {
|
817 |
+
// WC2.2 and older: recalculate to exclude tax
|
818 |
+
$discount_value = 0;
|
819 |
+
|
820 |
+
$items = $this->order->get_items();;
|
821 |
+
if( sizeof( $items ) > 0 ) {
|
822 |
+
foreach( $items as $item ) {
|
823 |
+
$discount_value += ($item['line_subtotal'] - $item['line_total']);
|
824 |
+
}
|
825 |
+
}
|
826 |
+
}
|
827 |
+
}
|
828 |
+
|
829 |
+
$discount = array (
|
830 |
+
'label' => __('Discount', 'woocommerce-pdf-invoices-packing-slips' ),
|
831 |
+
'value' => $this->format_price( $discount_value ),
|
832 |
+
'raw_value' => $discount_value,
|
833 |
+
);
|
834 |
+
|
835 |
+
if ( round( $discount_value, 3 ) != 0 ) {
|
836 |
+
return apply_filters( 'wpo_wcpdf_order_discount', $discount, $type, $tax, $this );
|
837 |
+
}
|
838 |
+
}
|
839 |
+
public function order_discount( $type = 'total', $tax = 'incl' ) {
|
840 |
+
$discount = $this->get_order_discount( $type, $tax );
|
841 |
+
echo $discount['value'];
|
842 |
+
}
|
843 |
+
|
844 |
+
/**
|
845 |
+
* Return the order fees
|
846 |
+
*/
|
847 |
+
public function get_order_fees( $tax = 'excl' ) {
|
848 |
+
if ( $_fees = $this->order->get_fees() ) {
|
849 |
+
foreach( $_fees as $id => $fee ) {
|
850 |
+
if ($tax == 'excl' ) {
|
851 |
+
$fee_price = $this->format_price( $fee['line_total'] );
|
852 |
+
} else {
|
853 |
+
$fee_price = $this->format_price( $fee['line_total'] + $fee['line_tax'] );
|
854 |
+
}
|
855 |
+
|
856 |
+
$fees[ $id ] = array(
|
857 |
+
'label' => $fee['name'],
|
858 |
+
'value' => $fee_price,
|
859 |
+
'line_total' => $this->format_price( $fee['line_total'] ),
|
860 |
+
'line_tax' => $this->format_price( $fee['line_tax'] )
|
861 |
+
);
|
862 |
+
}
|
863 |
+
return $fees;
|
864 |
+
}
|
865 |
+
}
|
866 |
+
|
867 |
+
/**
|
868 |
+
* Return the order taxes
|
869 |
+
*/
|
870 |
+
public function get_order_taxes() {
|
871 |
+
$tax_label = __( 'VAT', 'woocommerce-pdf-invoices-packing-slips' ); // register alternate label translation
|
872 |
+
$tax_label = __( 'Tax rate', 'woocommerce-pdf-invoices-packing-slips' );
|
873 |
+
$tax_rate_ids = $this->get_tax_rate_ids();
|
874 |
+
if ( $order_taxes = $this->order->get_taxes() ) {
|
875 |
+
foreach ( $order_taxes as $key => $tax ) {
|
876 |
+
if ( WCX::is_wc_version_gte_3_0() ) {
|
877 |
+
$taxes[ $key ] = array(
|
878 |
+
'label' => $tax->get_label(),
|
879 |
+
'value' => $this->format_price( $tax->get_tax_total() + $tax->get_shipping_tax_total() ),
|
880 |
+
'rate_id' => $tax->get_rate_id(),
|
881 |
+
'tax_amount' => $tax->get_tax_total(),
|
882 |
+
'shipping_tax_amount' => $tax->get_shipping_tax_total(),
|
883 |
+
'rate' => isset( $tax_rate_ids[ $tax->get_rate_id() ] ) ? ( (float) $tax_rate_ids[$tax->get_rate_id()]['tax_rate'] ) . ' %': '',
|
884 |
+
);
|
885 |
+
} else {
|
886 |
+
$taxes[ $key ] = array(
|
887 |
+
'label' => isset( $tax[ 'label' ] ) ? $tax[ 'label' ] : $tax[ 'name' ],
|
888 |
+
'value' => $this->format_price( ( $tax[ 'tax_amount' ] + $tax[ 'shipping_tax_amount' ] ) ),
|
889 |
+
'rate_id' => $tax['rate_id'],
|
890 |
+
'tax_amount' => $tax['tax_amount'],
|
891 |
+
'shipping_tax_amount' => $tax['shipping_tax_amount'],
|
892 |
+
'rate' => isset( $tax_rate_ids[ $tax['rate_id'] ] ) ? ( (float) $tax_rate_ids[$tax['rate_id']]['tax_rate'] ) . ' %': '',
|
893 |
+
);
|
894 |
+
}
|
895 |
+
|
896 |
+
}
|
897 |
+
|
898 |
+
return apply_filters( 'wpo_wcpdf_order_taxes', $taxes, $this );
|
899 |
+
}
|
900 |
+
}
|
901 |
+
|
902 |
+
/**
|
903 |
+
* Return/show the order grand total
|
904 |
+
*/
|
905 |
+
public function get_order_grand_total( $tax = 'incl' ) {
|
906 |
+
if ( version_compare( WOOCOMMERCE_VERSION, '2.1' ) >= 0 ) {
|
907 |
+
// WC 2.1 or newer is used
|
908 |
+
$total_unformatted = $this->order->get_total();
|
909 |
+
} else {
|
910 |
+
// Backwards compatibility
|
911 |
+
$total_unformatted = $this->order->get_order_total();
|
912 |
+
}
|
913 |
+
|
914 |
+
if ($tax == 'excl' ) {
|
915 |
+
$total = $this->format_price( $total_unformatted - $this->order->get_total_tax() );
|
916 |
+
$label = __( 'Total ex. VAT', 'woocommerce-pdf-invoices-packing-slips' );
|
917 |
+
} else {
|
918 |
+
$total = $this->format_price( ( $total_unformatted ) );
|
919 |
+
$label = __( 'Total', 'woocommerce-pdf-invoices-packing-slips' );
|
920 |
+
}
|
921 |
+
|
922 |
+
$grand_total = array(
|
923 |
+
'label' => $label,
|
924 |
+
'value' => $total,
|
925 |
+
);
|
926 |
+
|
927 |
+
return apply_filters( 'wpo_wcpdf_order_grand_total', $grand_total, $tax, $this );
|
928 |
+
}
|
929 |
+
public function order_grand_total( $tax = 'incl' ) {
|
930 |
+
$grand_total = $this->get_order_grand_total( $tax );
|
931 |
+
echo $grand_total['value'];
|
932 |
+
}
|
933 |
+
|
934 |
+
|
935 |
+
/**
|
936 |
+
* Return/Show shipping notes
|
937 |
+
*/
|
938 |
+
public function get_shipping_notes() {
|
939 |
+
if ( $this->is_refund( $this->order ) ) {
|
940 |
+
// return reason for refund if order is a refund
|
941 |
+
if ( version_compare( WOOCOMMERCE_VERSION, '3.0', '>=' ) ) {
|
942 |
+
$shipping_notes = $this->order->get_reason();
|
943 |
+
} elseif ( method_exists($this->order, 'get_refund_reason') ) {
|
944 |
+
$shipping_notes = $this->order->get_refund_reason();
|
945 |
+
} else {
|
946 |
+
$shipping_notes = wpautop( wptexturize( WCX_Order::get_prop( $this->order, 'customer_note', 'view' ) ) );
|
947 |
+
}
|
948 |
+
} else {
|
949 |
+
$shipping_notes = wpautop( wptexturize( WCX_Order::get_prop( $this->order, 'customer_note', 'view' ) ) );
|
950 |
+
}
|
951 |
+
return apply_filters( 'wpo_wcpdf_shipping_notes', $shipping_notes, $this );
|
952 |
+
}
|
953 |
+
public function shipping_notes() {
|
954 |
+
echo $this->get_shipping_notes();
|
955 |
+
}
|
956 |
+
|
957 |
+
/**
|
958 |
+
* wrapper for wc_price, ensuring currency is always passed
|
959 |
+
*/
|
960 |
+
public function format_price( $price, $args = array() ) {
|
961 |
+
if ( function_exists( 'wc_price' ) ) { // WC 2.1+
|
962 |
+
$args['currency'] = WCX_Order::get_prop( $this->order, 'currency' );
|
963 |
+
$formatted_price = wc_price( $price, $args );
|
964 |
+
} else {
|
965 |
+
$formatted_price = woocommerce_price( $price );
|
966 |
+
}
|
967 |
+
|
968 |
+
return $formatted_price;
|
969 |
+
}
|
970 |
+
public function wc_price( $price, $args = array() ) {
|
971 |
+
return $this->format_price( $price, $args );
|
972 |
+
}
|
973 |
+
|
974 |
+
/**
|
975 |
+
* Gets price - formatted for display.
|
976 |
+
*
|
977 |
+
* @access public
|
978 |
+
* @param mixed $item
|
979 |
+
* @return string
|
980 |
+
*/
|
981 |
+
public function get_formatted_item_price ( $item, $type, $tax_display = '' ) {
|
982 |
+
if ( ! isset( $item['line_subtotal'] ) || ! isset( $item['line_subtotal_tax'] ) ) {
|
983 |
+
return;
|
984 |
+
}
|
985 |
+
|
986 |
+
$divide_by = ($type == 'single' && $item['qty'] != 0 )?abs($item['qty']):1; //divide by 1 if $type is not 'single' (thus 'total')
|
987 |
+
if ( $tax_display == 'excl' ) {
|
988 |
+
$item_price = $this->format_price( ($this->order->get_line_subtotal( $item )) / $divide_by );
|
989 |
+
} else {
|
990 |
+
$item_price = $this->format_price( ($this->order->get_line_subtotal( $item, true )) / $divide_by );
|
991 |
+
}
|
992 |
+
|
993 |
+
return $item_price;
|
994 |
+
}
|
995 |
+
|
996 |
+
public function get_invoice_number() {
|
997 |
+
// Call the woocommerce_invoice_number filter and let third-party plugins set a number.
|
998 |
+
// Default is null, so we can detect whether a plugin has set the invoice number
|
999 |
+
$third_party_invoice_number = apply_filters( 'woocommerce_invoice_number', null, $this->order_id );
|
1000 |
+
if ($third_party_invoice_number !== null) {
|
1001 |
+
return $third_party_invoice_number;
|
1002 |
+
}
|
1003 |
+
|
1004 |
+
if ( $invoice_number = $this->get_number('invoice') ) {
|
1005 |
+
return $formatted_invoice_number = $invoice_number->get_formatted();
|
1006 |
+
} else {
|
1007 |
+
return '';
|
1008 |
+
}
|
1009 |
+
}
|
1010 |
+
|
1011 |
+
public function invoice_number() {
|
1012 |
+
echo $this->get_invoice_number();
|
1013 |
+
}
|
1014 |
+
|
1015 |
+
public function get_invoice_date() {
|
1016 |
+
if ( $invoice_date = $this->get_date('invoice') ) {
|
1017 |
+
return $invoice_date->date_i18n( apply_filters( 'wpo_wcpdf_date_format', wc_date_format(), $this ) );
|
1018 |
+
} else {
|
1019 |
+
return '';
|
1020 |
+
}
|
1021 |
+
}
|
1022 |
+
|
1023 |
+
public function invoice_date() {
|
1024 |
+
echo $this->get_invoice_date();
|
1025 |
+
}
|
1026 |
+
|
1027 |
+
|
1028 |
+
}
|
1029 |
+
|
1030 |
endif; // class_exists
|
includes/documents/abstract-wcpdf-order-document.php
CHANGED
@@ -1,676 +1,677 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPO\WC\PDF_Invoices\Documents;
|
3 |
-
|
4 |
-
use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
|
5 |
-
use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
|
6 |
-
use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
|
7 |
-
use WPO\WC\PDF_Invoices\Compatibility\WC_DateTime;
|
8 |
-
|
9 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
-
exit; // Exit if accessed directly
|
11 |
-
}
|
12 |
-
|
13 |
-
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Documents\\Order_Document' ) ) :
|
14 |
-
|
15 |
-
/**
|
16 |
-
* Abstract Document
|
17 |
-
*
|
18 |
-
* Handles generic pdf document & order data and database interaction
|
19 |
-
* which is extended by both Invoices & Packing Slips
|
20 |
-
*
|
21 |
-
* @class \WPO\WC\PDF_Invoices\Documents\Order_Document
|
22 |
-
* @version 2.0
|
23 |
-
* @category Class
|
24 |
-
* @author Ewout Fernhout
|
25 |
-
*/
|
26 |
-
|
27 |
-
abstract class Order_Document {
|
28 |
-
/**
|
29 |
-
* Document type.
|
30 |
-
* @var String
|
31 |
-
*/
|
32 |
-
public $type;
|
33 |
-
|
34 |
-
/**
|
35 |
-
* Document slug.
|
36 |
-
* @var String
|
37 |
-
*/
|
38 |
-
public $slug;
|
39 |
-
|
40 |
-
/**
|
41 |
-
* Document title.
|
42 |
-
* @var string
|
43 |
-
*/
|
44 |
-
public $title;
|
45 |
-
|
46 |
-
/**
|
47 |
-
* Document icon.
|
48 |
-
* @var string
|
49 |
-
*/
|
50 |
-
public $icon;
|
51 |
-
|
52 |
-
/**
|
53 |
-
* WC Order object
|
54 |
-
* @var object
|
55 |
-
*/
|
56 |
-
public $order;
|
57 |
-
|
58 |
-
/**
|
59 |
-
* WC Order ID
|
60 |
-
* @var object
|
61 |
-
*/
|
62 |
-
public $order_id;
|
63 |
-
|
64 |
-
/**
|
65 |
-
* Document settings.
|
66 |
-
* @var array
|
67 |
-
*/
|
68 |
-
public $settings;
|
69 |
-
|
70 |
-
/**
|
71 |
-
* TRUE if document is enabled.
|
72 |
-
* @var bool
|
73 |
-
*/
|
74 |
-
public $enabled;
|
75 |
-
|
76 |
-
/**
|
77 |
-
* Linked documents, used for data retrieval
|
78 |
-
* @var array
|
79 |
-
*/
|
80 |
-
protected $linked_documents = array();
|
81 |
-
|
82 |
-
/**
|
83 |
-
* Core data for this object. Name value pairs (name + default value).
|
84 |
-
* @var array
|
85 |
-
*/
|
86 |
-
protected $data = array();
|
87 |
-
|
88 |
-
/**
|
89 |
-
* Init/load the order object.
|
90 |
-
*
|
91 |
-
* @param int|object|WC_Order $order Order to init.
|
92 |
-
*/
|
93 |
-
public function __construct( $order = 0 ) {
|
94 |
-
if ( is_numeric( $order ) && $order > 0 ) {
|
95 |
-
$this->order_id = $order;
|
96 |
-
$this->order = WCX::get_order( $order_id );
|
97 |
-
} elseif ( $order instanceof \WC_Order || is_subclass_of( $order, '\WC_Abstract_Order') ) {
|
98 |
-
$this->order_id = WCX_Order::get_id( $order );
|
99 |
-
$this->order = $order;
|
100 |
-
}
|
101 |
-
|
102 |
-
// set properties
|
103 |
-
$this->slug = str_replace('-', '_', $this->type);
|
104 |
-
|
105 |
-
// load settings
|
106 |
-
$this->settings = $this->get_settings();
|
107 |
-
$this->enabled = $this->get_setting( 'enabled', false );
|
108 |
-
|
109 |
-
// load data
|
110 |
-
if ( $this->order ) {
|
111 |
-
$this->read_data( $this->order );
|
112 |
-
if ( WPO_WCPDF()->legacy_mode_enabled() ) {
|
113 |
-
global $wpo_wcpdf;
|
114 |
-
$wpo_wcpdf->export->order = $this->order;
|
115 |
-
$wpo_wcpdf->export->document = $this;
|
116 |
-
$wpo_wcpdf->export->order_id = $this->order_id;
|
117 |
-
$wpo_wcpdf->export->template_type = $this->type;
|
118 |
-
}
|
119 |
-
}
|
120 |
-
|
121 |
-
}
|
122 |
-
|
123 |
-
public function init_settings() {
|
124 |
-
return ;
|
125 |
-
}
|
126 |
-
|
127 |
-
public function get_settings() {
|
128 |
-
$common_settings = WPO_WCPDF()->settings->get_common_document_settings();
|
129 |
-
$document_settings = get_option( 'wpo_wcpdf_documents_settings_'.$this->get_type() );
|
130 |
-
return (array) $document_settings + (array) $common_settings;
|
131 |
-
}
|
132 |
-
|
133 |
-
public function get_setting( $key, $default = '' ) {
|
134 |
-
$setting = isset( $this->settings[$key] ) ? $this->settings[$key] : $default;
|
135 |
-
return $setting;
|
136 |
-
}
|
137 |
-
|
138 |
-
public function get_attach_to_email_ids() {
|
139 |
-
$email_ids = isset( $this->settings['attach_to_email_ids'] ) ? array_keys( $this->settings['attach_to_email_ids'] ) : array();
|
140 |
-
return $email_ids;
|
141 |
-
}
|
142 |
-
|
143 |
-
public function get_type() {
|
144 |
-
return $this->type;
|
145 |
-
}
|
146 |
-
|
147 |
-
public function is_enabled() {
|
148 |
-
return apply_filters( 'wpo_wcpdf_document_is_enabled', $this->enabled, $this->type );
|
149 |
-
}
|
150 |
-
|
151 |
-
public function get_hook_prefix() {
|
152 |
-
return 'wpo_wcpdf_' . $this->slug . '_get_';
|
153 |
-
}
|
154 |
-
|
155 |
-
public function read_data( $order ) {
|
156 |
-
$number = WCX_Order::get_meta( $order, "_wcpdf_{$this->slug}_number_data", true );
|
157 |
-
// fallback to legacy data for number
|
158 |
-
if ( empty( $number ) ) {
|
159 |
-
$number = WCX_Order::get_meta( $order, "_wcpdf_{$this->slug}_number", true );
|
160 |
-
$formatted_number = WCX_Order::get_meta( $order, "_wcpdf_formatted_{$this->slug}_number", true );
|
161 |
-
if (!empty($formatted_number)) {
|
162 |
-
$number = compact( 'number', 'formatted_number' );
|
163 |
-
}
|
164 |
-
}
|
165 |
-
|
166 |
-
// pass data to setter functions
|
167 |
-
$this->set_data( array(
|
168 |
-
// always load date before number, because date is used in number formatting
|
169 |
-
'date' => WCX_Order::get_meta( $order, "_wcpdf_{$this->slug}_date", true ),
|
170 |
-
'number' => $number,
|
171 |
-
), $order );
|
172 |
-
|
173 |
-
return;
|
174 |
-
}
|
175 |
-
|
176 |
-
public function init() {
|
177 |
-
$this->set_date( current_time( 'timestamp', true ) );
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
WCX_Order::update_meta_data( $order, "_wcpdf_{$this->slug}_{$key}
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
WCX_Order::update_meta_data( $order, "_wcpdf_{$this->slug}_{$key}
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
$
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
$this->linked_documents[ $document_type ]
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
|
267 |
-
| order
|
268 |
-
|
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
$
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
$css =
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
$
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
$
|
395 |
-
$
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
'
|
496 |
-
'
|
497 |
-
|
498 |
-
|
499 |
-
$
|
500 |
-
|
501 |
-
|
502 |
-
do_action( '
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
$
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
$
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
$
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
*
|
622 |
-
* @
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
$
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
'
|
633 |
-
'
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
$
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
|
1 |
+
<?php
|
2 |
+
namespace WPO\WC\PDF_Invoices\Documents;
|
3 |
+
|
4 |
+
use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
|
5 |
+
use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
|
6 |
+
use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
|
7 |
+
use WPO\WC\PDF_Invoices\Compatibility\WC_DateTime;
|
8 |
+
|
9 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
+
exit; // Exit if accessed directly
|
11 |
+
}
|
12 |
+
|
13 |
+
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Documents\\Order_Document' ) ) :
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Abstract Document
|
17 |
+
*
|
18 |
+
* Handles generic pdf document & order data and database interaction
|
19 |
+
* which is extended by both Invoices & Packing Slips
|
20 |
+
*
|
21 |
+
* @class \WPO\WC\PDF_Invoices\Documents\Order_Document
|
22 |
+
* @version 2.0
|
23 |
+
* @category Class
|
24 |
+
* @author Ewout Fernhout
|
25 |
+
*/
|
26 |
+
|
27 |
+
abstract class Order_Document {
|
28 |
+
/**
|
29 |
+
* Document type.
|
30 |
+
* @var String
|
31 |
+
*/
|
32 |
+
public $type;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Document slug.
|
36 |
+
* @var String
|
37 |
+
*/
|
38 |
+
public $slug;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Document title.
|
42 |
+
* @var string
|
43 |
+
*/
|
44 |
+
public $title;
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Document icon.
|
48 |
+
* @var string
|
49 |
+
*/
|
50 |
+
public $icon;
|
51 |
+
|
52 |
+
/**
|
53 |
+
* WC Order object
|
54 |
+
* @var object
|
55 |
+
*/
|
56 |
+
public $order;
|
57 |
+
|
58 |
+
/**
|
59 |
+
* WC Order ID
|
60 |
+
* @var object
|
61 |
+
*/
|
62 |
+
public $order_id;
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Document settings.
|
66 |
+
* @var array
|
67 |
+
*/
|
68 |
+
public $settings;
|
69 |
+
|
70 |
+
/**
|
71 |
+
* TRUE if document is enabled.
|
72 |
+
* @var bool
|
73 |
+
*/
|
74 |
+
public $enabled;
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Linked documents, used for data retrieval
|
78 |
+
* @var array
|
79 |
+
*/
|
80 |
+
protected $linked_documents = array();
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Core data for this object. Name value pairs (name + default value).
|
84 |
+
* @var array
|
85 |
+
*/
|
86 |
+
protected $data = array();
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Init/load the order object.
|
90 |
+
*
|
91 |
+
* @param int|object|WC_Order $order Order to init.
|
92 |
+
*/
|
93 |
+
public function __construct( $order = 0 ) {
|
94 |
+
if ( is_numeric( $order ) && $order > 0 ) {
|
95 |
+
$this->order_id = $order;
|
96 |
+
$this->order = WCX::get_order( $order_id );
|
97 |
+
} elseif ( $order instanceof \WC_Order || is_subclass_of( $order, '\WC_Abstract_Order') ) {
|
98 |
+
$this->order_id = WCX_Order::get_id( $order );
|
99 |
+
$this->order = $order;
|
100 |
+
}
|
101 |
+
|
102 |
+
// set properties
|
103 |
+
$this->slug = str_replace('-', '_', $this->type);
|
104 |
+
|
105 |
+
// load settings
|
106 |
+
$this->settings = $this->get_settings();
|
107 |
+
$this->enabled = $this->get_setting( 'enabled', false );
|
108 |
+
|
109 |
+
// load data
|
110 |
+
if ( $this->order ) {
|
111 |
+
$this->read_data( $this->order );
|
112 |
+
if ( WPO_WCPDF()->legacy_mode_enabled() ) {
|
113 |
+
global $wpo_wcpdf;
|
114 |
+
$wpo_wcpdf->export->order = $this->order;
|
115 |
+
$wpo_wcpdf->export->document = $this;
|
116 |
+
$wpo_wcpdf->export->order_id = $this->order_id;
|
117 |
+
$wpo_wcpdf->export->template_type = $this->type;
|
118 |
+
}
|
119 |
+
}
|
120 |
+
|
121 |
+
}
|
122 |
+
|
123 |
+
public function init_settings() {
|
124 |
+
return ;
|
125 |
+
}
|
126 |
+
|
127 |
+
public function get_settings() {
|
128 |
+
$common_settings = WPO_WCPDF()->settings->get_common_document_settings();
|
129 |
+
$document_settings = get_option( 'wpo_wcpdf_documents_settings_'.$this->get_type() );
|
130 |
+
return (array) $document_settings + (array) $common_settings;
|
131 |
+
}
|
132 |
+
|
133 |
+
public function get_setting( $key, $default = '' ) {
|
134 |
+
$setting = isset( $this->settings[$key] ) ? $this->settings[$key] : $default;
|
135 |
+
return $setting;
|
136 |
+
}
|
137 |
+
|
138 |
+
public function get_attach_to_email_ids() {
|
139 |
+
$email_ids = isset( $this->settings['attach_to_email_ids'] ) ? array_keys( $this->settings['attach_to_email_ids'] ) : array();
|
140 |
+
return $email_ids;
|
141 |
+
}
|
142 |
+
|
143 |
+
public function get_type() {
|
144 |
+
return $this->type;
|
145 |
+
}
|
146 |
+
|
147 |
+
public function is_enabled() {
|
148 |
+
return apply_filters( 'wpo_wcpdf_document_is_enabled', $this->enabled, $this->type );
|
149 |
+
}
|
150 |
+
|
151 |
+
public function get_hook_prefix() {
|
152 |
+
return 'wpo_wcpdf_' . $this->slug . '_get_';
|
153 |
+
}
|
154 |
+
|
155 |
+
public function read_data( $order ) {
|
156 |
+
$number = WCX_Order::get_meta( $order, "_wcpdf_{$this->slug}_number_data", true );
|
157 |
+
// fallback to legacy data for number
|
158 |
+
if ( empty( $number ) ) {
|
159 |
+
$number = WCX_Order::get_meta( $order, "_wcpdf_{$this->slug}_number", true );
|
160 |
+
$formatted_number = WCX_Order::get_meta( $order, "_wcpdf_formatted_{$this->slug}_number", true );
|
161 |
+
if (!empty($formatted_number)) {
|
162 |
+
$number = compact( 'number', 'formatted_number' );
|
163 |
+
}
|
164 |
+
}
|
165 |
+
|
166 |
+
// pass data to setter functions
|
167 |
+
$this->set_data( array(
|
168 |
+
// always load date before number, because date is used in number formatting
|
169 |
+
'date' => WCX_Order::get_meta( $order, "_wcpdf_{$this->slug}_date", true ),
|
170 |
+
'number' => $number,
|
171 |
+
), $order );
|
172 |
+
|
173 |
+
return;
|
174 |
+
}
|
175 |
+
|
176 |
+
public function init() {
|
177 |
+
$this->set_date( current_time( 'timestamp', true ) );
|
178 |
+
do_action( 'wpo_wcpdf_init_document', $this );
|
179 |
+
}
|
180 |
+
|
181 |
+
public function save( $order = null ) {
|
182 |
+
$order = empty( $order ) ? $this->order : $order;
|
183 |
+
if ( empty( $order ) ) {
|
184 |
+
return; // nowhere to save to...
|
185 |
+
}
|
186 |
+
|
187 |
+
foreach ($this->data as $key => $value) {
|
188 |
+
if ( empty( $value ) ) {
|
189 |
+
WCX_Order::delete_meta_data( $order, "_wcpdf_{$this->slug}_{$key}" );
|
190 |
+
if ( $key == 'date' ) {
|
191 |
+
WCX_Order::delete_meta_data( $order, "_wcpdf_{$this->slug}_{$key}_formatted" );
|
192 |
+
} elseif ( $key == 'number' ) {
|
193 |
+
WCX_Order::delete_meta_data( $order, "_wcpdf_{$this->slug}_{$key}_data" );
|
194 |
+
}
|
195 |
+
} else {
|
196 |
+
if ( $key == 'date' ) {
|
197 |
+
// store dates as timestamp and formatted as mysql time
|
198 |
+
WCX_Order::update_meta_data( $order, "_wcpdf_{$this->slug}_{$key}", $value->getTimestamp() );
|
199 |
+
WCX_Order::update_meta_data( $order, "_wcpdf_{$this->slug}_{$key}_formatted", $value->date( 'Y-m-d H:i:s' ) );
|
200 |
+
} elseif ( $key == 'number' ) {
|
201 |
+
// store both formatted number and number data
|
202 |
+
WCX_Order::update_meta_data( $order, "_wcpdf_{$this->slug}_{$key}", $value->formatted_number );
|
203 |
+
WCX_Order::update_meta_data( $order, "_wcpdf_{$this->slug}_{$key}_data", $value->to_array() );
|
204 |
+
}
|
205 |
+
}
|
206 |
+
}
|
207 |
+
}
|
208 |
+
|
209 |
+
public function exists() {
|
210 |
+
return !empty( $this->data['number'] );
|
211 |
+
}
|
212 |
+
|
213 |
+
/*
|
214 |
+
|--------------------------------------------------------------------------
|
215 |
+
| Data getters
|
216 |
+
|--------------------------------------------------------------------------
|
217 |
+
*/
|
218 |
+
|
219 |
+
public function get_data( $key, $document_type = '', $order = null, $context = 'view' ) {
|
220 |
+
$document_type = empty( $document_type ) ? $this->type : $document_type;
|
221 |
+
$order = empty( $order ) ? $this->order : $order;
|
222 |
+
|
223 |
+
// redirect get_data call for linked documents
|
224 |
+
if ( $document_type != $this->type ) {
|
225 |
+
if ( !isset( $this->linked_documents[ $document_type ] ) ) {
|
226 |
+
// always assume parent for documents linked to credit notes
|
227 |
+
if ($this->type == 'credit-note') {
|
228 |
+
$order = $this->get_refund_parent( $order );
|
229 |
+
}
|
230 |
+
// order is not loaded to avoid overhead - we pass this by reference directly to the read_data method instead
|
231 |
+
$this->linked_documents[ $document_type ] = wcpdf_get_document( $document_type, null );
|
232 |
+
$this->linked_documents[ $document_type ]->read_data( $order );
|
233 |
+
}
|
234 |
+
return $this->linked_documents[ $document_type ]->get_data( $key, $document_type );
|
235 |
+
}
|
236 |
+
|
237 |
+
$value = null;
|
238 |
+
|
239 |
+
if ( array_key_exists( $key, $this->data ) ) {
|
240 |
+
$value = $this->data[ $key ];
|
241 |
+
|
242 |
+
if ( 'view' === $context ) {
|
243 |
+
$value = apply_filters( $this->get_hook_prefix() . $key, $value, $this );
|
244 |
+
}
|
245 |
+
}
|
246 |
+
|
247 |
+
return $value;
|
248 |
+
}
|
249 |
+
|
250 |
+
public function get_number( $document_type = '', $order = null, $context = 'view' ) {
|
251 |
+
return $this->get_data( 'number', $document_type, $order, $context );
|
252 |
+
}
|
253 |
+
|
254 |
+
public function get_date( $document_type = '', $order = null, $context = 'view' ) {
|
255 |
+
return $this->get_data( 'date', $document_type, $order, $context );
|
256 |
+
}
|
257 |
+
|
258 |
+
public function get_title() {
|
259 |
+
return apply_filters( "wpo_wcpdf_{$this->slug}_title", $this->title, $this );
|
260 |
+
}
|
261 |
+
|
262 |
+
/*
|
263 |
+
|--------------------------------------------------------------------------
|
264 |
+
| Data setters
|
265 |
+
|--------------------------------------------------------------------------
|
266 |
+
|
|
267 |
+
| Functions for setting order data. These should not update anything in the
|
268 |
+
| order itself and should only change what is stored in the class
|
269 |
+
| object.
|
270 |
+
*/
|
271 |
+
|
272 |
+
public function set_data( $data, $order ) {
|
273 |
+
$order = empty( $order ) ? $this->order : $order;
|
274 |
+
foreach ($data as $key => $value) {
|
275 |
+
$setter = "set_$key";
|
276 |
+
if ( is_callable( array( $this, $setter ) ) ) {
|
277 |
+
$this->$setter( $value, $order );
|
278 |
+
} else {
|
279 |
+
$this->data[ $key ] = $value;
|
280 |
+
}
|
281 |
+
}
|
282 |
+
}
|
283 |
+
|
284 |
+
public function set_date( $value, $order = null ) {
|
285 |
+
$order = empty( $order ) ? $this->order : $order;
|
286 |
+
try {
|
287 |
+
if ( empty( $value ) ) {
|
288 |
+
$this->data[ 'date' ] = null;
|
289 |
+
return;
|
290 |
+
}
|
291 |
+
|
292 |
+
if ( is_a( $value, 'WC_DateTime' ) ) {
|
293 |
+
$datetime = $value;
|
294 |
+
} elseif ( is_numeric( $value ) ) {
|
295 |
+
// Timestamps are handled as UTC timestamps in all cases.
|
296 |
+
$datetime = new WC_DateTime( "@{$value}", new \DateTimeZone( 'UTC' ) );
|
297 |
+
} else {
|
298 |
+
// Strings are defined in local WP timezone. Convert to UTC.
|
299 |
+
if ( 1 === preg_match( '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(Z|((-|\+)\d{2}:\d{2}))$/', $value, $date_bits ) ) {
|
300 |
+
$offset = ! empty( $date_bits[7] ) ? iso8601_timezone_to_offset( $date_bits[7] ) : wc_timezone_offset();
|
301 |
+
$timestamp = gmmktime( $date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1] ) - $offset;
|
302 |
+
} else {
|
303 |
+
$timestamp = wc_string_to_timestamp( get_gmt_from_date( gmdate( 'Y-m-d H:i:s', wc_string_to_timestamp( $value ) ) ) );
|
304 |
+
}
|
305 |
+
$datetime = new WC_DateTime( "@{$timestamp}", new \DateTimeZone( 'UTC' ) );
|
306 |
+
}
|
307 |
+
|
308 |
+
// Set local timezone or offset.
|
309 |
+
if ( get_option( 'timezone_string' ) ) {
|
310 |
+
$datetime->setTimezone( new \DateTimeZone( wc_timezone_string() ) );
|
311 |
+
} else {
|
312 |
+
$datetime->set_utc_offset( wc_timezone_offset() );
|
313 |
+
}
|
314 |
+
|
315 |
+
$this->data[ 'date' ] = $datetime;
|
316 |
+
} catch ( Exception $e ) {}
|
317 |
+
|
318 |
+
|
319 |
+
}
|
320 |
+
|
321 |
+
public function set_number( $value, $order = null ) {
|
322 |
+
$order = empty( $order ) ? $this->order : $order;
|
323 |
+
|
324 |
+
if ( is_array( $value ) ) {
|
325 |
+
$filtered_value = array_filter( $value );
|
326 |
+
}
|
327 |
+
|
328 |
+
if ( empty( $value ) || ( is_array( $value ) && empty( $filtered_value ) ) ) {
|
329 |
+
$document_number = null;
|
330 |
+
} elseif ( $value instanceof Document_Number ) {
|
331 |
+
// WCPDF 2.0 number data
|
332 |
+
$document_number = $value;
|
333 |
+
} elseif ( is_array( $value ) ) {
|
334 |
+
// WCPDF 2.0 number data as array
|
335 |
+
$document_number = new Document_Number( $value, $this->get_number_settings(), $this, $order );
|
336 |
+
} else {
|
337 |
+
// plain number
|
338 |
+
$document_number = new Document_Number( $value, $this->get_number_settings(), $this, $order );
|
339 |
+
}
|
340 |
+
|
341 |
+
$this->data[ 'number' ] = $document_number;
|
342 |
+
}
|
343 |
+
|
344 |
+
/*
|
345 |
+
|--------------------------------------------------------------------------
|
346 |
+
| Settings getters / outputters
|
347 |
+
|--------------------------------------------------------------------------
|
348 |
+
*/
|
349 |
+
|
350 |
+
public function get_number_settings() {
|
351 |
+
$number_settings = isset($this->settings['number_format'])?$this->settings['number_format']:array();
|
352 |
+
return apply_filters( 'wpo_wcpdf_document_number_settings', $number_settings, $this );
|
353 |
+
}
|
354 |
+
|
355 |
+
/**
|
356 |
+
* Output template styles
|
357 |
+
*/
|
358 |
+
public function template_styles() {
|
359 |
+
$css = apply_filters( 'wpo_wcpdf_template_styles_file', $this->locate_template_file( "style.css" ) );
|
360 |
+
|
361 |
+
ob_start();
|
362 |
+
if (file_exists($css)) {
|
363 |
+
include($css);
|
364 |
+
}
|
365 |
+
$css = ob_get_clean();
|
366 |
+
$css = apply_filters( 'wpo_wcpdf_template_styles', $css, $this );
|
367 |
+
|
368 |
+
echo $css;
|
369 |
+
}
|
370 |
+
|
371 |
+
public function has_header_logo() {
|
372 |
+
return !empty( $this->settings['header_logo'] );
|
373 |
+
}
|
374 |
+
|
375 |
+
/**
|
376 |
+
* Return logo id
|
377 |
+
*/
|
378 |
+
public function get_header_logo_id() {
|
379 |
+
if ( !empty( $this->settings['header_logo'] ) ) {
|
380 |
+
return apply_filters( 'wpo_wcpdf_header_logo_id', $this->settings['header_logo'], $this );
|
381 |
+
}
|
382 |
+
}
|
383 |
+
|
384 |
+
/**
|
385 |
+
* Show logo html
|
386 |
+
*/
|
387 |
+
public function header_logo() {
|
388 |
+
if ($this->get_header_logo_id()) {
|
389 |
+
$attachment_id = $this->get_header_logo_id();
|
390 |
+
$company = $this->get_shop_name();
|
391 |
+
if( $attachment_id ) {
|
392 |
+
$attachment = wp_get_attachment_image_src( $attachment_id, 'full', false );
|
393 |
+
|
394 |
+
$attachment_src = $attachment[0];
|
395 |
+
$attachment_width = $attachment[1];
|
396 |
+
$attachment_height = $attachment[2];
|
397 |
+
|
398 |
+
$attachment_path = get_attached_file( $attachment_id );
|
399 |
+
|
400 |
+
if ( apply_filters('wpo_wcpdf_use_path', true) && file_exists($attachment_path) ) {
|
401 |
+
$src = $attachment_path;
|
402 |
+
} else {
|
403 |
+
$src = $attachment_src;
|
404 |
+
}
|
405 |
+
|
406 |
+
printf('<img src="%1$s" width="%2$d" height="%3$d" alt="%4$s" />', $src, $attachment_width, $attachment_height, esc_attr( $company ) );
|
407 |
+
}
|
408 |
+
}
|
409 |
+
}
|
410 |
+
|
411 |
+
public function get_settings_text( $settings_key, $default = false, $autop = true ) {
|
412 |
+
if ( !empty( $this->settings[$settings_key]['default'] ) ) {
|
413 |
+
$text = wptexturize( trim( $this->settings[$settings_key]['default'] ) );
|
414 |
+
if ($autop === true) {
|
415 |
+
$text = wpautop( $text );
|
416 |
+
}
|
417 |
+
} else {
|
418 |
+
$text = $default;
|
419 |
+
}
|
420 |
+
return apply_filters( "wpo_wcpdf_{$settings_key}", $text, $this );
|
421 |
+
}
|
422 |
+
|
423 |
+
/**
|
424 |
+
* Return/Show custom company name or default to blog name
|
425 |
+
*/
|
426 |
+
public function get_shop_name() {
|
427 |
+
$default = get_bloginfo( 'name' );
|
428 |
+
return $this->get_settings_text( 'shop_name', $default, false );
|
429 |
+
}
|
430 |
+
public function shop_name() {
|
431 |
+
echo $this->get_shop_name();
|
432 |
+
}
|
433 |
+
|
434 |
+
/**
|
435 |
+
* Return/Show shop/company address if provided
|
436 |
+
*/
|
437 |
+
public function get_shop_address() {
|
438 |
+
return $this->get_settings_text( 'shop_address' );
|
439 |
+
}
|
440 |
+
public function shop_address() {
|
441 |
+
echo $this->get_shop_address();
|
442 |
+
}
|
443 |
+
|
444 |
+
/**
|
445 |
+
* Return/Show shop/company footer imprint, copyright etc.
|
446 |
+
*/
|
447 |
+
public function get_footer() {
|
448 |
+
return $this->get_settings_text( 'footer' );
|
449 |
+
}
|
450 |
+
public function footer() {
|
451 |
+
echo $this->get_footer();
|
452 |
+
}
|
453 |
+
|
454 |
+
/**
|
455 |
+
* Return/Show Extra field 1
|
456 |
+
*/
|
457 |
+
public function get_extra_1() {
|
458 |
+
return $this->get_settings_text( 'extra_1' );
|
459 |
+
|
460 |
+
}
|
461 |
+
public function extra_1() {
|
462 |
+
echo $this->get_extra_1();
|
463 |
+
}
|
464 |
+
|
465 |
+
/**
|
466 |
+
* Return/Show Extra field 2
|
467 |
+
*/
|
468 |
+
public function get_extra_2() {
|
469 |
+
return $this->get_settings_text( 'extra_2' );
|
470 |
+
}
|
471 |
+
public function extra_2() {
|
472 |
+
echo $this->get_extra_2();
|
473 |
+
}
|
474 |
+
|
475 |
+
/**
|
476 |
+
* Return/Show Extra field 3
|
477 |
+
*/
|
478 |
+
public function get_extra_3() {
|
479 |
+
return $this->get_settings_text( 'extra_3' );
|
480 |
+
}
|
481 |
+
public function extra_3() {
|
482 |
+
echo $this->get_extra_3();
|
483 |
+
}
|
484 |
+
|
485 |
+
/*
|
486 |
+
|--------------------------------------------------------------------------
|
487 |
+
| Output functions
|
488 |
+
|--------------------------------------------------------------------------
|
489 |
+
*/
|
490 |
+
|
491 |
+
public function get_pdf() {
|
492 |
+
do_action( 'wpo_wcpdf_before_pdf', $this->get_type(), $this );
|
493 |
+
|
494 |
+
$pdf_settings = array(
|
495 |
+
'paper_size' => apply_filters( 'wpo_wcpdf_paper_format', $this->get_setting( 'paper_size', 'A4' ), $this->get_type() ),
|
496 |
+
'paper_orientation' => apply_filters( 'wpo_wcpdf_paper_orientation', 'portrait', $this->get_type() ),
|
497 |
+
'font_subsetting' => $this->get_setting( 'font_subsetting', false ),
|
498 |
+
);
|
499 |
+
$pdf_maker = wcpdf_get_pdf_maker( $this->get_html(), $pdf_settings );
|
500 |
+
$pdf = $pdf_maker->output();
|
501 |
+
|
502 |
+
do_action( 'wpo_wcpdf_after_pdf', $this->get_type(), $this );
|
503 |
+
do_action( 'wpo_wcpdf_pdf_created', $pdf, $this );
|
504 |
+
|
505 |
+
return $pdf;
|
506 |
+
}
|
507 |
+
|
508 |
+
public function get_html( $args = array() ) {
|
509 |
+
do_action( 'wpo_wcpdf_before_html', $this->get_type(), $this );
|
510 |
+
$default_args = array (
|
511 |
+
'wrap_html_content' => true,
|
512 |
+
);
|
513 |
+
$args = $args + $default_args;
|
514 |
+
|
515 |
+
$html = $this->render_template( $this->locate_template_file( "{$this->type}.php" ) );
|
516 |
+
if ($args['wrap_html_content']) {
|
517 |
+
$html = $this->wrap_html_content( $html );
|
518 |
+
}
|
519 |
+
|
520 |
+
// clean up special characters
|
521 |
+
if ( function_exists('utf8_decode') && function_exists('mb_convert_encoding') ) {
|
522 |
+
$html = utf8_decode(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
|
523 |
+
}
|
524 |
+
|
525 |
+
do_action( 'wpo_wcpdf_after_html', $this->get_type(), $this );
|
526 |
+
|
527 |
+
return apply_filters( 'wpo_wcpdf_get_html', $html, $this );
|
528 |
+
}
|
529 |
+
|
530 |
+
public function output_pdf( $output_mode = 'download' ) {
|
531 |
+
$pdf = $this->get_pdf();
|
532 |
+
wcpdf_pdf_headers( $this->get_filename(), $output_mode, $pdf );
|
533 |
+
echo $pdf;
|
534 |
+
die();
|
535 |
+
}
|
536 |
+
|
537 |
+
public function output_html() {
|
538 |
+
echo $this->get_html();
|
539 |
+
die();
|
540 |
+
}
|
541 |
+
|
542 |
+
public function wrap_html_content( $content ) {
|
543 |
+
if ( WPO_WCPDF()->legacy_mode_enabled() ) {
|
544 |
+
$GLOBALS['wpo_wcpdf']->export->output_body = $content;
|
545 |
+
}
|
546 |
+
|
547 |
+
$html = $this->render_template( $this->locate_template_file( "html-document-wrapper.php" ), array(
|
548 |
+
'content' => $content,
|
549 |
+
)
|
550 |
+
);
|
551 |
+
return $html;
|
552 |
+
}
|
553 |
+
|
554 |
+
public function get_filename( $context = 'download', $args = array() ) {
|
555 |
+
$order_count = isset($args['order_ids']) ? count($args['order_ids']) : 1;
|
556 |
+
|
557 |
+
$name = $this->get_type();
|
558 |
+
if ( get_post_type( $this->order_id ) == 'shop_order_refund' ) {
|
559 |
+
$number = $this->order_id;
|
560 |
+
} else {
|
561 |
+
$number = method_exists( $this->order, 'get_order_number' ) ? $this->order->get_order_number() : '';
|
562 |
+
}
|
563 |
+
|
564 |
+
if ( $order_count == 1 ) {
|
565 |
+
$suffix = $number;
|
566 |
+
} else {
|
567 |
+
$suffix = date('Y-m-d'); // 2020-11-11
|
568 |
+
}
|
569 |
+
|
570 |
+
$filename = $name . '-' . $suffix . '.pdf';
|
571 |
+
|
572 |
+
// Filter filename
|
573 |
+
$order_ids = isset($args['order_ids']) ? $args['order_ids'] : array( $this->order_id );
|
574 |
+
$filename = apply_filters( 'wpo_wcpdf_filename', $filename, $this->get_type(), $order_ids, $context );
|
575 |
+
|
576 |
+
// sanitize filename (after filters to prevent human errors)!
|
577 |
+
return sanitize_file_name( $filename );
|
578 |
+
}
|
579 |
+
|
580 |
+
public function get_template_path() {
|
581 |
+
return WPO_WCPDF()->settings->get_template_path();
|
582 |
+
}
|
583 |
+
|
584 |
+
public function locate_template_file( $file ) {
|
585 |
+
if (empty($file)) {
|
586 |
+
$file = $this->type.'.php';
|
587 |
+
}
|
588 |
+
$path = WPO_WCPDF()->settings->get_template_path( $file );
|
589 |
+
$file_path = "{$path}/{$file}";
|
590 |
+
|
591 |
+
$fallback_file_path = WPO_WCPDF()->plugin_path() . '/templates/Simple/' . $file;
|
592 |
+
if ( !file_exists( $file_path ) && file_exists( $fallback_file_path ) ) {
|
593 |
+
$file_path = $fallback_file_path;
|
594 |
+
}
|
595 |
+
|
596 |
+
$file_path = apply_filters( 'wpo_wcpdf_template_file', $file_path, $this->type, $this->order );
|
597 |
+
|
598 |
+
return $file_path;
|
599 |
+
}
|
600 |
+
|
601 |
+
public function render_template( $file, $args = array() ) {
|
602 |
+
do_action( 'wpo_wcpdf_process_template', $this->get_type(), $this );
|
603 |
+
|
604 |
+
if ( ! empty( $args ) && is_array( $args ) ) {
|
605 |
+
extract( $args );
|
606 |
+
}
|
607 |
+
ob_start();
|
608 |
+
if (file_exists($file)) {
|
609 |
+
include($file);
|
610 |
+
}
|
611 |
+
return ob_get_clean();
|
612 |
+
}
|
613 |
+
|
614 |
+
/*
|
615 |
+
|--------------------------------------------------------------------------
|
616 |
+
| Settings helper functions
|
617 |
+
|--------------------------------------------------------------------------
|
618 |
+
*/
|
619 |
+
|
620 |
+
/**
|
621 |
+
* get all emails registered in WooCommerce
|
622 |
+
* @param boolean $remove_defaults switch to remove default woocommerce emails
|
623 |
+
* @return array $emails list of all email ids/slugs and names
|
624 |
+
*/
|
625 |
+
public function get_wc_emails() {
|
626 |
+
// get emails from WooCommerce
|
627 |
+
global $woocommerce;
|
628 |
+
$mailer = $woocommerce->mailer();
|
629 |
+
$wc_emails = $mailer->get_emails();
|
630 |
+
|
631 |
+
$non_order_emails = array(
|
632 |
+
'customer_note',
|
633 |
+
'customer_reset_password',
|
634 |
+
'customer_new_account'
|
635 |
+
);
|
636 |
+
|
637 |
+
$emails = array();
|
638 |
+
foreach ($wc_emails as $class => $email) {
|
639 |
+
if ( !in_array( $email->id, $non_order_emails ) ) {
|
640 |
+
switch ($email->id) {
|
641 |
+
case 'new_order':
|
642 |
+
$emails[$email->id] = sprintf('%s (%s)', $email->title, __( 'Admin email', 'woocommerce-pdf-invoices-packing-slips' ) );
|
643 |
+
break;
|
644 |
+
case 'customer_invoice':
|
645 |
+
$emails[$email->id] = sprintf('%s (%s)', $email->title, __( 'Manual email', 'woocommerce-pdf-invoices-packing-slips' ) );
|
646 |
+
break;
|
647 |
+
default:
|
648 |
+
$emails[$email->id] = $email->title;
|
649 |
+
break;
|
650 |
+
}
|
651 |
+
}
|
652 |
+
}
|
653 |
+
|
654 |
+
return apply_filters( 'wpo_wcpdf_wc_emails', $emails );
|
655 |
+
}
|
656 |
+
|
657 |
+
// get list of WooCommerce statuses
|
658 |
+
public function get_wc_order_status_list() {
|
659 |
+
if ( version_compare( WOOCOMMERCE_VERSION, '2.2', '<' ) ) {
|
660 |
+
$statuses = (array) get_terms( 'shop_order_status', array( 'hide_empty' => 0, 'orderby' => 'id' ) );
|
661 |
+
foreach ( $statuses as $status ) {
|
662 |
+
$order_statuses[esc_attr( $status->slug )] = esc_html__( $status->name, 'woocommerce' );
|
663 |
+
}
|
664 |
+
} else {
|
665 |
+
$statuses = wc_get_order_statuses();
|
666 |
+
foreach ( $statuses as $status_slug => $status ) {
|
667 |
+
$status_slug = 'wc-' === substr( $status_slug, 0, 3 ) ? substr( $status_slug, 3 ) : $status_slug;
|
668 |
+
$order_statuses[$status_slug] = $status;
|
669 |
+
}
|
670 |
+
}
|
671 |
+
return $order_statuses;
|
672 |
+
}
|
673 |
+
|
674 |
+
|
675 |
+
}
|
676 |
+
|
677 |
+
endif; // class_exists
|
includes/documents/class-wcpdf-invoice.php
CHANGED
@@ -1,353 +1,353 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPO\WC\PDF_Invoices\Documents;
|
3 |
-
|
4 |
-
use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
|
5 |
-
use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
|
6 |
-
use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
|
7 |
-
|
8 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
9 |
-
exit; // Exit if accessed directly
|
10 |
-
}
|
11 |
-
|
12 |
-
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Documents\\Invoice' ) ) :
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Invoice Document
|
16 |
-
*
|
17 |
-
* @class \WPO\WC\PDF_Invoices\Documents\Invoice
|
18 |
-
* @version 2.0
|
19 |
-
* @category Class
|
20 |
-
* @author Ewout Fernhout
|
21 |
-
*/
|
22 |
-
|
23 |
-
class Invoice extends Order_Document_Methods {
|
24 |
-
/**
|
25 |
-
* Init/load the order object.
|
26 |
-
*
|
27 |
-
* @param int|object|WC_Order $order Order to init.
|
28 |
-
*/
|
29 |
-
public function __construct( $order = 0 ) {
|
30 |
-
// set properties
|
31 |
-
$this->type = 'invoice';
|
32 |
-
$this->title = __( 'Invoice', 'woocommerce-pdf-invoices-packing-slips' );
|
33 |
-
$this->icon = WPO_WCPDF()->plugin_url() . "/assets/images/invoice.png";
|
34 |
-
|
35 |
-
// Call parent constructor
|
36 |
-
parent::__construct( $order );
|
37 |
-
}
|
38 |
-
|
39 |
-
public function get_title() {
|
40 |
-
// override/not using $this->title to allow for language switching!
|
41 |
-
return apply_filters( "wpo_wcpdf_{$this->slug}_title", __( 'Invoice', 'woocommerce-pdf-invoices-packing-slips' ), $this );
|
42 |
-
}
|
43 |
-
|
44 |
-
public function init() {
|
45 |
-
$this->set_date( current_time( 'timestamp', true ) );
|
46 |
-
$this->init_number();
|
47 |
-
}
|
48 |
-
|
49 |
-
public function init_number() {
|
50 |
-
global $wpdb;
|
51 |
-
// If a third-party plugin claims to generate invoice numbers, trigger this instead
|
52 |
-
if ( apply_filters( 'woocommerce_invoice_number_by_plugin', false ) || apply_filters( 'wpo_wcpdf_external_invoice_number_enabled', false, $this ) ) {
|
53 |
-
$invoice_number = apply_filters( 'woocommerce_generate_invoice_number', null, $this->order );
|
54 |
-
$invoice_number = apply_filters( 'wpo_wcpdf_external_invoice_number', $invoice_number, $this );
|
55 |
-
if ( is_numeric($invoice_number) || $invoice_number instanceof Document_Number ) {
|
56 |
-
$this->set_number( $invoice_number );
|
57 |
-
} else {
|
58 |
-
// invoice number is not numeric, treat as formatted
|
59 |
-
// try to extract meaningful number data
|
60 |
-
$formatted_number = $invoice_number;
|
61 |
-
$number = (int) preg_replace('/\D/', '', $invoice_number);
|
62 |
-
$invoice_number = compact( 'number', 'formatted_number' );
|
63 |
-
$this->set_number( $invoice_number );
|
64 |
-
}
|
65 |
-
return $invoice_number;
|
66 |
-
}
|
67 |
-
|
68 |
-
$number_store_method = WPO_WCPDF()->settings->get_sequential_number_store_method();
|
69 |
-
$number_store = new Sequential_Number_Store( 'invoice_number', $number_store_method );
|
70 |
-
// reset invoice number yearly
|
71 |
-
if ( isset( $this->settings['reset_number_yearly'] ) ) {
|
72 |
-
$current_year = date("Y");
|
73 |
-
$last_number_year = $number_store->get_last_date('Y');
|
74 |
-
// check if we need to reset
|
75 |
-
if ( $current_year != $last_number_year ) {
|
76 |
-
$number_store->set_next( 1 );
|
77 |
-
}
|
78 |
-
}
|
79 |
-
|
80 |
-
$invoice_date = $this->get_date();
|
81 |
-
$invoice_number = $number_store->increment( $this->order_id, $invoice_date->date_i18n( 'Y-m-d H:i:s' ) );
|
82 |
-
|
83 |
-
$this->set_number( $invoice_number );
|
84 |
-
|
85 |
-
return $invoice_number;
|
86 |
-
}
|
87 |
-
|
88 |
-
public function get_settings() {
|
89 |
-
$common_settings = WPO_WCPDF()->settings->get_common_document_settings();
|
90 |
-
$document_settings = get_option( 'wpo_wcpdf_documents_settings_invoice' );
|
91 |
-
return (array) $document_settings + (array) $common_settings;
|
92 |
-
}
|
93 |
-
|
94 |
-
public function get_filename( $context = 'download', $args = array() ) {
|
95 |
-
$order_count = isset($args['order_ids']) ? count($args['order_ids']) : 1;
|
96 |
-
|
97 |
-
$name = _n( 'invoice', 'invoices', $order_count, 'woocommerce-pdf-invoices-packing-slips' );
|
98 |
-
|
99 |
-
if ( $order_count == 1 ) {
|
100 |
-
if ( isset( $this->settings['display_number'] ) ) {
|
101 |
-
$suffix = (string) $this->get_number();
|
102 |
-
} else {
|
103 |
-
if ( empty( $this->order ) ) {
|
104 |
-
$order = WCX::get_order ( $order_ids[0] );
|
105 |
-
$suffix = method_exists( $order, 'get_order_number' ) ? $order->get_order_number() : '';
|
106 |
-
} else {
|
107 |
-
$suffix = method_exists( $this->order, 'get_order_number' ) ? $this->order->get_order_number() : '';
|
108 |
-
}
|
109 |
-
}
|
110 |
-
} else {
|
111 |
-
$suffix = date('Y-m-d'); // 2020-11-11
|
112 |
-
}
|
113 |
-
|
114 |
-
$filename = $name . '-' . $suffix . '.pdf';
|
115 |
-
|
116 |
-
// Filter filename
|
117 |
-
$order_ids = isset($args['order_ids']) ? $args['order_ids'] : array( $this->order_id );
|
118 |
-
$filename = apply_filters( 'wpo_wcpdf_filename', $filename, $this->get_type(), $order_ids, $context );
|
119 |
-
|
120 |
-
// sanitize filename (after filters to prevent human errors)!
|
121 |
-
return sanitize_file_name( $filename );
|
122 |
-
}
|
123 |
-
|
124 |
-
|
125 |
-
/**
|
126 |
-
* Initialise settings
|
127 |
-
*/
|
128 |
-
public function init_settings() {
|
129 |
-
// Register settings.
|
130 |
-
$page = $option_group = $option_name = 'wpo_wcpdf_documents_settings_invoice';
|
131 |
-
|
132 |
-
$settings_fields = array(
|
133 |
-
array(
|
134 |
-
'type' => 'section',
|
135 |
-
'id' => 'invoice',
|
136 |
-
'title' => '',
|
137 |
-
'callback' => 'section',
|
138 |
-
),
|
139 |
-
array(
|
140 |
-
'type' => 'setting',
|
141 |
-
'id' => 'enabled',
|
142 |
-
'title' => __( 'Enable', 'woocommerce-pdf-invoices-packing-slips' ),
|
143 |
-
'callback' => 'checkbox',
|
144 |
-
'section' => 'invoice',
|
145 |
-
'args' => array(
|
146 |
-
'option_name' => $option_name,
|
147 |
-
'id' => 'enabled',
|
148 |
-
)
|
149 |
-
),
|
150 |
-
array(
|
151 |
-
'type' => 'setting',
|
152 |
-
'id' => 'attach_to_email_ids',
|
153 |
-
'title' => __( 'Attach to:', 'woocommerce-pdf-invoices-packing-slips' ),
|
154 |
-
'callback' => 'multiple_checkboxes',
|
155 |
-
'section' => 'invoice',
|
156 |
-
'args' => array(
|
157 |
-
'option_name' => $option_name,
|
158 |
-
'id' => 'attach_to_email_ids',
|
159 |
-
'fields' => $this->get_wc_emails(),
|
160 |
-
'description' => !is_writable( WPO_WCPDF()->main->get_tmp_path( 'attachments' ) ) ? '<span class="wpo-warning">' . sprintf( __( 'It looks like the temp folder (<code>%s</code>) is not writable, check the permissions for this folder! Without having write access to this folder, the plugin will not be able to email invoices.', 'woocommerce-pdf-invoices-packing-slips' ), WPO_WCPDF()->main->get_tmp_path( 'attachments' ) ).'</span>':'',
|
161 |
-
)
|
162 |
-
),
|
163 |
-
array(
|
164 |
-
'type' => 'setting',
|
165 |
-
'id' => 'display_shipping_address',
|
166 |
-
'title' => __( 'Display shipping address', 'woocommerce-pdf-invoices-packing-slips' ),
|
167 |
-
'callback' => 'checkbox',
|
168 |
-
'section' => 'invoice',
|
169 |
-
'args' => array(
|
170 |
-
'option_name' => $option_name,
|
171 |
-
'id' => 'display_shipping_address',
|
172 |
-
'description' => __( 'Display shipping address (in addition to the default billing address) if different from billing address', 'woocommerce-pdf-invoices-packing-slips' ),
|
173 |
-
)
|
174 |
-
),
|
175 |
-
array(
|
176 |
-
'type' => 'setting',
|
177 |
-
'id' => 'display_email',
|
178 |
-
'title' => __( 'Display email address', 'woocommerce-pdf-invoices-packing-slips' ),
|
179 |
-
'callback' => 'checkbox',
|
180 |
-
'section' => 'invoice',
|
181 |
-
'args' => array(
|
182 |
-
'option_name' => $option_name,
|
183 |
-
'id' => 'display_email',
|
184 |
-
)
|
185 |
-
),
|
186 |
-
array(
|
187 |
-
'type' => 'setting',
|
188 |
-
'id' => 'display_phone',
|
189 |
-
'title' => __( 'Display phone number', 'woocommerce-pdf-invoices-packing-slips' ),
|
190 |
-
'callback' => 'checkbox',
|
191 |
-
'section' => 'invoice',
|
192 |
-
'args' => array(
|
193 |
-
'option_name' => $option_name,
|
194 |
-
'id' => 'display_phone',
|
195 |
-
)
|
196 |
-
),
|
197 |
-
array(
|
198 |
-
'type' => 'setting',
|
199 |
-
'id' => 'display_date',
|
200 |
-
'title' => __( 'Display invoice date', 'woocommerce-pdf-invoices-packing-slips' ),
|
201 |
-
'callback' => 'checkbox',
|
202 |
-
'section' => 'invoice',
|
203 |
-
'args' => array(
|
204 |
-
'option_name' => $option_name,
|
205 |
-
'id' => 'display_date',
|
206 |
-
'value' => 'invoice_date',
|
207 |
-
)
|
208 |
-
),
|
209 |
-
array(
|
210 |
-
'type' => 'setting',
|
211 |
-
'id' => 'display_number',
|
212 |
-
'title' => __( 'Display invoice number', 'woocommerce-pdf-invoices-packing-slips' ),
|
213 |
-
'callback' => 'checkbox',
|
214 |
-
'section' => 'invoice',
|
215 |
-
'args' => array(
|
216 |
-
'option_name' => $option_name,
|
217 |
-
'id' => 'display_number',
|
218 |
-
'value' => 'invoice_number',
|
219 |
-
)
|
220 |
-
),
|
221 |
-
array(
|
222 |
-
'type' => 'setting',
|
223 |
-
'id' => 'next_invoice_number',
|
224 |
-
'title' => __( 'Next invoice number (without prefix/suffix etc.)', 'woocommerce-pdf-invoices-packing-slips' ),
|
225 |
-
'callback' => 'next_number_edit',
|
226 |
-
'section' => 'invoice',
|
227 |
-
'args' => array(
|
228 |
-
'store' => 'invoice_number',
|
229 |
-
'size' => '10',
|
230 |
-
'description' => __( 'This is the number that will be used for the next document. By default, numbering starts from 1 and increases for every new document. Note that if you override this and set it lower than the current/highest number, this could create duplicate numbers!', 'woocommerce-pdf-invoices-packing-slips' ),
|
231 |
-
)
|
232 |
-
),
|
233 |
-
array(
|
234 |
-
'type' => 'setting',
|
235 |
-
'id' => 'number_format',
|
236 |
-
'title' => __( 'Number format', 'woocommerce-pdf-invoices-packing-slips' ),
|
237 |
-
'callback' => 'multiple_text_input',
|
238 |
-
'section' => 'invoice',
|
239 |
-
'args' => array(
|
240 |
-
'option_name' => $option_name,
|
241 |
-
'id' => 'number_format',
|
242 |
-
'fields' => array(
|
243 |
-
'prefix' => array(
|
244 |
-
'placeholder' => __( 'Prefix' , 'woocommerce-pdf-invoices-packing-slips' ),
|
245 |
-
'size' => 20,
|
246 |
-
'description' => __( 'to use the invoice year and/or month, use [invoice_year] or [invoice_month] respectively' , 'woocommerce-pdf-invoices-packing-slips' ),
|
247 |
-
),
|
248 |
-
'suffix' => array(
|
249 |
-
'placeholder' => __( 'Suffix' , 'woocommerce-pdf-invoices-packing-slips' ),
|
250 |
-
'size' => 20,
|
251 |
-
'description' => '',
|
252 |
-
),
|
253 |
-
'padding' => array(
|
254 |
-
'placeholder' => __( 'Padding' , 'woocommerce-pdf-invoices-packing-slips' ),
|
255 |
-
'size' => 20,
|
256 |
-
'type' => 'number',
|
257 |
-
'description' => __( 'enter the number of digits here - enter "6" to display 42 as 000042' , 'woocommerce-pdf-invoices-packing-slips' ),
|
258 |
-
),
|
259 |
-
),
|
260 |
-
'description' => __( 'note: if you have already created a custom invoice number format with a filter, the above settings will be ignored' , 'woocommerce-pdf-invoices-packing-slips' ),
|
261 |
-
)
|
262 |
-
),
|
263 |
-
array(
|
264 |
-
'type' => 'setting',
|
265 |
-
'id' => 'reset_number_yearly',
|
266 |
-
'title' => __( 'Reset invoice number yearly', 'woocommerce-pdf-invoices-packing-slips' ),
|
267 |
-
'callback' => 'checkbox',
|
268 |
-
'section' => 'invoice',
|
269 |
-
'args' => array(
|
270 |
-
'option_name' => $option_name,
|
271 |
-
'id' => 'reset_number_yearly',
|
272 |
-
)
|
273 |
-
),
|
274 |
-
array(
|
275 |
-
'type' => 'setting',
|
276 |
-
'id' => 'my_account_buttons',
|
277 |
-
'title' => __( 'Allow My Account invoice download', 'woocommerce-pdf-invoices-packing-slips' ),
|
278 |
-
'callback' => 'select',
|
279 |
-
'section' => 'invoice',
|
280 |
-
'args' => array(
|
281 |
-
'option_name' => $option_name,
|
282 |
-
'id' => 'my_account_buttons',
|
283 |
-
'options' => array(
|
284 |
-
'available' => __( 'Only when an invoice is already created/emailed' , 'woocommerce-pdf-invoices-packing-slips' ),
|
285 |
-
'custom' => __( 'Only for specific order statuses (define below)' , 'woocommerce-pdf-invoices-packing-slips' ),
|
286 |
-
'always' => __( 'Always' , 'woocommerce-pdf-invoices-packing-slips' ),
|
287 |
-
'never' => __( 'Never' , 'woocommerce-pdf-invoices-packing-slips' ),
|
288 |
-
),
|
289 |
-
'custom' => array(
|
290 |
-
'type' => 'multiple_checkboxes',
|
291 |
-
'args' => array(
|
292 |
-
'option_name' => $option_name,
|
293 |
-
'id' => 'my_account_restrict',
|
294 |
-
'fields' => $this->get_wc_order_status_list(),
|
295 |
-
),
|
296 |
-
),
|
297 |
-
)
|
298 |
-
),
|
299 |
-
array(
|
300 |
-
'type' => 'setting',
|
301 |
-
'id' => 'invoice_number_column',
|
302 |
-
'title' => __( 'Enable invoice number column in the orders list', 'woocommerce-pdf-invoices-packing-slips' ),
|
303 |
-
'callback' => 'checkbox',
|
304 |
-
'section' => 'invoice',
|
305 |
-
'args' => array(
|
306 |
-
'option_name' => $option_name,
|
307 |
-
'id' => 'invoice_number_column',
|
308 |
-
)
|
309 |
-
),
|
310 |
-
array(
|
311 |
-
'type' => 'setting',
|
312 |
-
'id' => 'disable_free',
|
313 |
-
'title' => __( 'Disable for free products', 'woocommerce-pdf-invoices-packing-slips' ),
|
314 |
-
'callback' => 'checkbox',
|
315 |
-
'section' => 'invoice',
|
316 |
-
'args' => array(
|
317 |
-
'option_name' => $option_name,
|
318 |
-
'id' => 'disable_free',
|
319 |
-
'description' => __( "Disable automatic creation/attachment when only free products are ordered", 'woocommerce-pdf-invoices-packing-slips' ),
|
320 |
-
)
|
321 |
-
),
|
322 |
-
);
|
323 |
-
|
324 |
-
|
325 |
-
// remove/rename some fields when invoice number is controlled externally
|
326 |
-
if( apply_filters('woocommerce_invoice_number_by_plugin', false) ) {
|
327 |
-
$remove_settings = array( 'next_invoice_number', 'number_format', 'reset_number_yearly' );
|
328 |
-
foreach ($settings_fields as $key => $settings_field) {
|
329 |
-
if (in_array($settings_field['id'], $remove_settings)) {
|
330 |
-
unset($settings_fields[$key]);
|
331 |
-
} elseif ( $settings_field['id'] == 'display_number' ) {
|
332 |
-
// alternate description for invoice number
|
333 |
-
$invoice_number_desc = __( 'Invoice numbers are created by a third-party extension.', 'woocommerce-pdf-invoices-packing-slips' );
|
334 |
-
if ( esc_attr( apply_filters( 'woocommerce_invoice_number_configuration_link', null ) ) ) {
|
335 |
-
$invoice_number_desc .= ' '.sprintf(__( 'Configure it <a href="%s">here</a>.', 'woocommerce-pdf-invoices-packing-slips' ), $config_link);
|
336 |
-
}
|
337 |
-
$settings_fields[$key]['args']['description'] = '<i>'.$invoice_number_desc.'</i>';
|
338 |
-
}
|
339 |
-
}
|
340 |
-
}
|
341 |
-
|
342 |
-
// allow plugins to alter settings fields
|
343 |
-
$settings_fields = apply_filters( 'wpo_wcpdf_settings_fields_documents_invoice', $settings_fields, $page, $option_group, $option_name );
|
344 |
-
WPO_WCPDF()->settings->add_settings_fields( $settings_fields, $page, $option_group, $option_name );
|
345 |
-
return;
|
346 |
-
|
347 |
-
}
|
348 |
-
|
349 |
-
}
|
350 |
-
|
351 |
-
endif; // class_exists
|
352 |
-
|
353 |
return new Invoice();
|
1 |
+
<?php
|
2 |
+
namespace WPO\WC\PDF_Invoices\Documents;
|
3 |
+
|
4 |
+
use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
|
5 |
+
use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
|
6 |
+
use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
|
7 |
+
|
8 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
9 |
+
exit; // Exit if accessed directly
|
10 |
+
}
|
11 |
+
|
12 |
+
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Documents\\Invoice' ) ) :
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Invoice Document
|
16 |
+
*
|
17 |
+
* @class \WPO\WC\PDF_Invoices\Documents\Invoice
|
18 |
+
* @version 2.0
|
19 |
+
* @category Class
|
20 |
+
* @author Ewout Fernhout
|
21 |
+
*/
|
22 |
+
|
23 |
+
class Invoice extends Order_Document_Methods {
|
24 |
+
/**
|
25 |
+
* Init/load the order object.
|
26 |
+
*
|
27 |
+
* @param int|object|WC_Order $order Order to init.
|
28 |
+
*/
|
29 |
+
public function __construct( $order = 0 ) {
|
30 |
+
// set properties
|
31 |
+
$this->type = 'invoice';
|
32 |
+
$this->title = __( 'Invoice', 'woocommerce-pdf-invoices-packing-slips' );
|
33 |
+
$this->icon = WPO_WCPDF()->plugin_url() . "/assets/images/invoice.png";
|
34 |
+
|
35 |
+
// Call parent constructor
|
36 |
+
parent::__construct( $order );
|
37 |
+
}
|
38 |
+
|
39 |
+
public function get_title() {
|
40 |
+
// override/not using $this->title to allow for language switching!
|
41 |
+
return apply_filters( "wpo_wcpdf_{$this->slug}_title", __( 'Invoice', 'woocommerce-pdf-invoices-packing-slips' ), $this );
|
42 |
+
}
|
43 |
+
|
44 |
+
public function init() {
|
45 |
+
$this->set_date( current_time( 'timestamp', true ) );
|
46 |
+
$this->init_number();
|
47 |
+
}
|
48 |
+
|
49 |
+
public function init_number() {
|
50 |
+
global $wpdb;
|
51 |
+
// If a third-party plugin claims to generate invoice numbers, trigger this instead
|
52 |
+
if ( apply_filters( 'woocommerce_invoice_number_by_plugin', false ) || apply_filters( 'wpo_wcpdf_external_invoice_number_enabled', false, $this ) ) {
|
53 |
+
$invoice_number = apply_filters( 'woocommerce_generate_invoice_number', null, $this->order );
|
54 |
+
$invoice_number = apply_filters( 'wpo_wcpdf_external_invoice_number', $invoice_number, $this );
|
55 |
+
if ( is_numeric($invoice_number) || $invoice_number instanceof Document_Number ) {
|
56 |
+
$this->set_number( $invoice_number );
|
57 |
+
} else {
|
58 |
+
// invoice number is not numeric, treat as formatted
|
59 |
+
// try to extract meaningful number data
|
60 |
+
$formatted_number = $invoice_number;
|
61 |
+
$number = (int) preg_replace('/\D/', '', $invoice_number);
|
62 |
+
$invoice_number = compact( 'number', 'formatted_number' );
|
63 |
+
$this->set_number( $invoice_number );
|
64 |
+
}
|
65 |
+
return $invoice_number;
|
66 |
+
}
|
67 |
+
|
68 |
+
$number_store_method = WPO_WCPDF()->settings->get_sequential_number_store_method();
|
69 |
+
$number_store = new Sequential_Number_Store( 'invoice_number', $number_store_method );
|
70 |
+
// reset invoice number yearly
|
71 |
+
if ( isset( $this->settings['reset_number_yearly'] ) ) {
|
72 |
+
$current_year = date("Y");
|
73 |
+
$last_number_year = $number_store->get_last_date('Y');
|
74 |
+
// check if we need to reset
|
75 |
+
if ( $current_year != $last_number_year ) {
|
76 |
+
$number_store->set_next( 1 );
|
77 |
+
}
|
78 |
+
}
|
79 |
+
|
80 |
+
$invoice_date = $this->get_date();
|
81 |
+
$invoice_number = $number_store->increment( $this->order_id, $invoice_date->date_i18n( 'Y-m-d H:i:s' ) );
|
82 |
+
|
83 |
+
$this->set_number( $invoice_number );
|
84 |
+
|
85 |
+
return $invoice_number;
|
86 |
+
}
|
87 |
+
|
88 |
+
public function get_settings() {
|
89 |
+
$common_settings = WPO_WCPDF()->settings->get_common_document_settings();
|
90 |
+
$document_settings = get_option( 'wpo_wcpdf_documents_settings_invoice' );
|
91 |
+
return (array) $document_settings + (array) $common_settings;
|
92 |
+
}
|
93 |
+
|
94 |
+
public function get_filename( $context = 'download', $args = array() ) {
|
95 |
+
$order_count = isset($args['order_ids']) ? count($args['order_ids']) : 1;
|
96 |
+
|
97 |
+
$name = _n( 'invoice', 'invoices', $order_count, 'woocommerce-pdf-invoices-packing-slips' );
|
98 |
+
|
99 |
+
if ( $order_count == 1 ) {
|
100 |
+
if ( isset( $this->settings['display_number'] ) ) {
|
101 |
+
$suffix = (string) $this->get_number();
|
102 |
+
} else {
|
103 |
+
if ( empty( $this->order ) ) {
|
104 |
+
$order = WCX::get_order ( $order_ids[0] );
|
105 |
+
$suffix = method_exists( $order, 'get_order_number' ) ? $order->get_order_number() : '';
|
106 |
+
} else {
|
107 |
+
$suffix = method_exists( $this->order, 'get_order_number' ) ? $this->order->get_order_number() : '';
|
108 |
+
}
|
109 |
+
}
|
110 |
+
} else {
|
111 |
+
$suffix = date('Y-m-d'); // 2020-11-11
|
112 |
+
}
|
113 |
+
|
114 |
+
$filename = $name . '-' . $suffix . '.pdf';
|
115 |
+
|
116 |
+
// Filter filename
|
117 |
+
$order_ids = isset($args['order_ids']) ? $args['order_ids'] : array( $this->order_id );
|
118 |
+
$filename = apply_filters( 'wpo_wcpdf_filename', $filename, $this->get_type(), $order_ids, $context );
|
119 |
+
|
120 |
+
// sanitize filename (after filters to prevent human errors)!
|
121 |
+
return sanitize_file_name( $filename );
|
122 |
+
}
|
123 |
+
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Initialise settings
|
127 |
+
*/
|
128 |
+
public function init_settings() {
|
129 |
+
// Register settings.
|
130 |
+
$page = $option_group = $option_name = 'wpo_wcpdf_documents_settings_invoice';
|
131 |
+
|
132 |
+
$settings_fields = array(
|
133 |
+
array(
|
134 |
+
'type' => 'section',
|
135 |
+
'id' => 'invoice',
|
136 |
+
'title' => '',
|
137 |
+
'callback' => 'section',
|
138 |
+
),
|
139 |
+
array(
|
140 |
+
'type' => 'setting',
|
141 |
+
'id' => 'enabled',
|
142 |
+
'title' => __( 'Enable', 'woocommerce-pdf-invoices-packing-slips' ),
|
143 |
+
'callback' => 'checkbox',
|
144 |
+
'section' => 'invoice',
|
145 |
+
'args' => array(
|
146 |
+
'option_name' => $option_name,
|
147 |
+
'id' => 'enabled',
|
148 |
+
)
|
149 |
+
),
|
150 |
+
array(
|
151 |
+
'type' => 'setting',
|
152 |
+
'id' => 'attach_to_email_ids',
|
153 |
+
'title' => __( 'Attach to:', 'woocommerce-pdf-invoices-packing-slips' ),
|
154 |
+
'callback' => 'multiple_checkboxes',
|
155 |
+
'section' => 'invoice',
|
156 |
+
'args' => array(
|
157 |
+
'option_name' => $option_name,
|
158 |
+
'id' => 'attach_to_email_ids',
|
159 |
+
'fields' => $this->get_wc_emails(),
|
160 |
+
'description' => !is_writable( WPO_WCPDF()->main->get_tmp_path( 'attachments' ) ) ? '<span class="wpo-warning">' . sprintf( __( 'It looks like the temp folder (<code>%s</code>) is not writable, check the permissions for this folder! Without having write access to this folder, the plugin will not be able to email invoices.', 'woocommerce-pdf-invoices-packing-slips' ), WPO_WCPDF()->main->get_tmp_path( 'attachments' ) ).'</span>':'',
|
161 |
+
)
|
162 |
+
),
|
163 |
+
array(
|
164 |
+
'type' => 'setting',
|
165 |
+
'id' => 'display_shipping_address',
|
166 |
+
'title' => __( 'Display shipping address', 'woocommerce-pdf-invoices-packing-slips' ),
|
167 |
+
'callback' => 'checkbox',
|
168 |
+
'section' => 'invoice',
|
169 |
+
'args' => array(
|
170 |
+
'option_name' => $option_name,
|
171 |
+
'id' => 'display_shipping_address',
|
172 |
+
'description' => __( 'Display shipping address (in addition to the default billing address) if different from billing address', 'woocommerce-pdf-invoices-packing-slips' ),
|
173 |
+
)
|
174 |
+
),
|
175 |
+
array(
|
176 |
+
'type' => 'setting',
|
177 |
+
'id' => 'display_email',
|
178 |
+
'title' => __( 'Display email address', 'woocommerce-pdf-invoices-packing-slips' ),
|
179 |
+
'callback' => 'checkbox',
|
180 |
+
'section' => 'invoice',
|
181 |
+
'args' => array(
|
182 |
+
'option_name' => $option_name,
|
183 |
+
'id' => 'display_email',
|
184 |
+
)
|
185 |
+
),
|
186 |
+
array(
|
187 |
+
'type' => 'setting',
|
188 |
+
'id' => 'display_phone',
|
189 |
+
'title' => __( 'Display phone number', 'woocommerce-pdf-invoices-packing-slips' ),
|
190 |
+
'callback' => 'checkbox',
|
191 |
+
'section' => 'invoice',
|
192 |
+
'args' => array(
|
193 |
+
'option_name' => $option_name,
|
194 |
+
'id' => 'display_phone',
|
195 |
+
)
|
196 |
+
),
|
197 |
+
array(
|
198 |
+
'type' => 'setting',
|
199 |
+
'id' => 'display_date',
|
200 |
+
'title' => __( 'Display invoice date', 'woocommerce-pdf-invoices-packing-slips' ),
|
201 |
+
'callback' => 'checkbox',
|
202 |
+
'section' => 'invoice',
|
203 |
+
'args' => array(
|
204 |
+
'option_name' => $option_name,
|
205 |
+
'id' => 'display_date',
|
206 |
+
'value' => 'invoice_date',
|
207 |
+
)
|
208 |
+
),
|
209 |
+
array(
|
210 |
+
'type' => 'setting',
|
211 |
+
'id' => 'display_number',
|
212 |
+
'title' => __( 'Display invoice number', 'woocommerce-pdf-invoices-packing-slips' ),
|
213 |
+
'callback' => 'checkbox',
|
214 |
+
'section' => 'invoice',
|
215 |
+
'args' => array(
|
216 |
+
'option_name' => $option_name,
|
217 |
+
'id' => 'display_number',
|
218 |
+
'value' => 'invoice_number',
|
219 |
+
)
|
220 |
+
),
|
221 |
+
array(
|
222 |
+
'type' => 'setting',
|
223 |
+
'id' => 'next_invoice_number',
|
224 |
+
'title' => __( 'Next invoice number (without prefix/suffix etc.)', 'woocommerce-pdf-invoices-packing-slips' ),
|
225 |
+
'callback' => 'next_number_edit',
|
226 |
+
'section' => 'invoice',
|
227 |
+
'args' => array(
|
228 |
+
'store' => 'invoice_number',
|
229 |
+
'size' => '10',
|
230 |
+
'description' => __( 'This is the number that will be used for the next document. By default, numbering starts from 1 and increases for every new document. Note that if you override this and set it lower than the current/highest number, this could create duplicate numbers!', 'woocommerce-pdf-invoices-packing-slips' ),
|
231 |
+
)
|
232 |
+
),
|
233 |
+
array(
|
234 |
+
'type' => 'setting',
|
235 |
+
'id' => 'number_format',
|
236 |
+
'title' => __( 'Number format', 'woocommerce-pdf-invoices-packing-slips' ),
|
237 |
+
'callback' => 'multiple_text_input',
|
238 |
+
'section' => 'invoice',
|
239 |
+
'args' => array(
|
240 |
+
'option_name' => $option_name,
|
241 |
+
'id' => 'number_format',
|
242 |
+
'fields' => array(
|
243 |
+
'prefix' => array(
|
244 |
+
'placeholder' => __( 'Prefix' , 'woocommerce-pdf-invoices-packing-slips' ),
|
245 |
+
'size' => 20,
|
246 |
+
'description' => __( 'to use the invoice year and/or month, use [invoice_year] or [invoice_month] respectively' , 'woocommerce-pdf-invoices-packing-slips' ),
|
247 |
+
),
|
248 |
+
'suffix' => array(
|
249 |
+
'placeholder' => __( 'Suffix' , 'woocommerce-pdf-invoices-packing-slips' ),
|
250 |
+
'size' => 20,
|
251 |
+
'description' => '',
|
252 |
+
),
|
253 |
+
'padding' => array(
|
254 |
+
'placeholder' => __( 'Padding' , 'woocommerce-pdf-invoices-packing-slips' ),
|
255 |
+
'size' => 20,
|
256 |
+
'type' => 'number',
|
257 |
+
'description' => __( 'enter the number of digits here - enter "6" to display 42 as 000042' , 'woocommerce-pdf-invoices-packing-slips' ),
|
258 |
+
),
|
259 |
+
),
|
260 |
+
'description' => __( 'note: if you have already created a custom invoice number format with a filter, the above settings will be ignored' , 'woocommerce-pdf-invoices-packing-slips' ),
|
261 |
+
)
|
262 |
+
),
|
263 |
+
array(
|
264 |
+
'type' => 'setting',
|
265 |
+
'id' => 'reset_number_yearly',
|
266 |
+
'title' => __( 'Reset invoice number yearly', 'woocommerce-pdf-invoices-packing-slips' ),
|
267 |
+
'callback' => 'checkbox',
|
268 |
+
'section' => 'invoice',
|
269 |
+
'args' => array(
|
270 |
+
'option_name' => $option_name,
|
271 |
+
'id' => 'reset_number_yearly',
|
272 |
+
)
|
273 |
+
),
|
274 |
+
array(
|
275 |
+
'type' => 'setting',
|
276 |
+
'id' => 'my_account_buttons',
|
277 |
+
'title' => __( 'Allow My Account invoice download', 'woocommerce-pdf-invoices-packing-slips' ),
|
278 |
+
'callback' => 'select',
|
279 |
+
'section' => 'invoice',
|
280 |
+
'args' => array(
|
281 |
+
'option_name' => $option_name,
|
282 |
+
'id' => 'my_account_buttons',
|
283 |
+
'options' => array(
|
284 |
+
'available' => __( 'Only when an invoice is already created/emailed' , 'woocommerce-pdf-invoices-packing-slips' ),
|
285 |
+
'custom' => __( 'Only for specific order statuses (define below)' , 'woocommerce-pdf-invoices-packing-slips' ),
|
286 |
+
'always' => __( 'Always' , 'woocommerce-pdf-invoices-packing-slips' ),
|
287 |
+
'never' => __( 'Never' , 'woocommerce-pdf-invoices-packing-slips' ),
|
288 |
+
),
|
289 |
+
'custom' => array(
|
290 |
+
'type' => 'multiple_checkboxes',
|
291 |
+
'args' => array(
|
292 |
+
'option_name' => $option_name,
|
293 |
+
'id' => 'my_account_restrict',
|
294 |
+
'fields' => $this->get_wc_order_status_list(),
|
295 |
+
),
|
296 |
+
),
|
297 |
+
)
|
298 |
+
),
|
299 |
+
array(
|
300 |
+
'type' => 'setting',
|
301 |
+
'id' => 'invoice_number_column',
|
302 |
+
'title' => __( 'Enable invoice number column in the orders list', 'woocommerce-pdf-invoices-packing-slips' ),
|
303 |
+
'callback' => 'checkbox',
|
304 |
+
'section' => 'invoice',
|
305 |
+
'args' => array(
|
306 |
+
'option_name' => $option_name,
|
307 |
+
'id' => 'invoice_number_column',
|
308 |
+
)
|
309 |
+
),
|
310 |
+
array(
|
311 |
+
'type' => 'setting',
|
312 |
+
'id' => 'disable_free',
|
313 |
+
'title' => __( 'Disable for free products', 'woocommerce-pdf-invoices-packing-slips' ),
|
314 |
+
'callback' => 'checkbox',
|
315 |
+
'section' => 'invoice',
|
316 |
+
'args' => array(
|
317 |
+
'option_name' => $option_name,
|
318 |
+
'id' => 'disable_free',
|
319 |
+
'description' => __( "Disable automatic creation/attachment when only free products are ordered", 'woocommerce-pdf-invoices-packing-slips' ),
|
320 |
+
)
|
321 |
+
),
|
322 |
+
);
|
323 |
+
|
324 |
+
|
325 |
+
// remove/rename some fields when invoice number is controlled externally
|
326 |
+
if( apply_filters('woocommerce_invoice_number_by_plugin', false) ) {
|
327 |
+
$remove_settings = array( 'next_invoice_number', 'number_format', 'reset_number_yearly' );
|
328 |
+
foreach ($settings_fields as $key => $settings_field) {
|
329 |
+
if (in_array($settings_field['id'], $remove_settings)) {
|
330 |
+
unset($settings_fields[$key]);
|
331 |
+
} elseif ( $settings_field['id'] == 'display_number' ) {
|
332 |
+
// alternate description for invoice number
|
333 |
+
$invoice_number_desc = __( 'Invoice numbers are created by a third-party extension.', 'woocommerce-pdf-invoices-packing-slips' );
|
334 |
+
if ( esc_attr( apply_filters( 'woocommerce_invoice_number_configuration_link', null ) ) ) {
|
335 |
+
$invoice_number_desc .= ' '.sprintf(__( 'Configure it <a href="%s">here</a>.', 'woocommerce-pdf-invoices-packing-slips' ), $config_link);
|
336 |
+
}
|
337 |
+
$settings_fields[$key]['args']['description'] = '<i>'.$invoice_number_desc.'</i>';
|
338 |
+
}
|
339 |
+
}
|
340 |
+
}
|
341 |
+
|
342 |
+
// allow plugins to alter settings fields
|
343 |
+
$settings_fields = apply_filters( 'wpo_wcpdf_settings_fields_documents_invoice', $settings_fields, $page, $option_group, $option_name );
|
344 |
+
WPO_WCPDF()->settings->add_settings_fields( $settings_fields, $page, $option_group, $option_name );
|
345 |
+
return;
|
346 |
+
|
347 |
+
}
|
348 |
+
|
349 |
+
}
|
350 |
+
|
351 |
+
endif; // class_exists
|
352 |
+
|
353 |
return new Invoice();
|
includes/documents/class-wcpdf-packing-slip.php
CHANGED
@@ -1,150 +1,150 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPO\WC\PDF_Invoices\Documents;
|
3 |
-
|
4 |
-
use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
|
5 |
-
use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
|
6 |
-
use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
|
7 |
-
|
8 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
9 |
-
exit; // Exit if accessed directly
|
10 |
-
}
|
11 |
-
|
12 |
-
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Documents\\Packing_Slip' ) ) :
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Packing Slip Document
|
16 |
-
*
|
17 |
-
* @class \WPO\WC\PDF_Invoices\Documents\Packing_Slip
|
18 |
-
* @version 2.0
|
19 |
-
* @category Class
|
20 |
-
* @author Ewout Fernhout
|
21 |
-
*/
|
22 |
-
|
23 |
-
class Packing_Slip extends Order_Document_Methods {
|
24 |
-
/**
|
25 |
-
* Init/load the order object.
|
26 |
-
*
|
27 |
-
* @param int|object|WC_Order $order Order to init.
|
28 |
-
*/
|
29 |
-
public function __construct( $order = 0 ) {
|
30 |
-
// set properties
|
31 |
-
$this->type = 'packing-slip';
|
32 |
-
$this->title = __( 'Packing Slip', 'woocommerce-pdf-invoices-packing-slips' );
|
33 |
-
$this->icon = WPO_WCPDF()->plugin_url() . "/assets/images/packing-slip.png";
|
34 |
-
|
35 |
-
// Call parent constructor
|
36 |
-
parent::__construct( $order );
|
37 |
-
}
|
38 |
-
|
39 |
-
public function get_title() {
|
40 |
-
// override/not using $this->title to allow for language switching!
|
41 |
-
return apply_filters( "wpo_wcpdf_{$this->slug}_title", __( 'Packing Slip', 'woocommerce-pdf-invoices-packing-slips' ), $this );
|
42 |
-
}
|
43 |
-
|
44 |
-
public function get_settings() {
|
45 |
-
$common_settings = WPO_WCPDF()->settings->get_common_document_settings();
|
46 |
-
$document_settings = get_option( 'wpo_wcpdf_documents_settings_packing-slip' );
|
47 |
-
return (array) $document_settings + (array) $common_settings;
|
48 |
-
}
|
49 |
-
|
50 |
-
public function get_filename( $context = 'download', $args = array() ) {
|
51 |
-
$order_count = isset($args['order_ids']) ? count($args['order_ids']) : 1;
|
52 |
-
|
53 |
-
$name = _n( 'packing-slip', 'packing-slips', $order_count, 'woocommerce-pdf-invoices-packing-slips' );
|
54 |
-
|
55 |
-
if ( $order_count == 1 ) {
|
56 |
-
if ( isset( $this->settings['display_number'] ) ) {
|
57 |
-
$suffix = (string) $this->get_number();
|
58 |
-
} else {
|
59 |
-
if ( empty( $this->order ) ) {
|
60 |
-
$order = WCX::get_order ( $order_ids[0] );
|
61 |
-
$suffix = method_exists( $order, 'get_order_number' ) ? $order->get_order_number() : '';
|
62 |
-
} else {
|
63 |
-
$suffix = method_exists( $this->order, 'get_order_number' ) ? $this->order->get_order_number() : '';
|
64 |
-
}
|
65 |
-
}
|
66 |
-
} else {
|
67 |
-
$suffix = date('Y-m-d'); // 2020-11-11
|
68 |
-
}
|
69 |
-
|
70 |
-
$filename = $name . '-' . $suffix . '.pdf';
|
71 |
-
|
72 |
-
// Filter filename
|
73 |
-
$order_ids = isset($args['order_ids']) ? $args['order_ids'] : array( $this->order_id );
|
74 |
-
$filename = apply_filters( 'wpo_wcpdf_filename', $filename, $this->get_type(), $order_ids, $context );
|
75 |
-
|
76 |
-
// sanitize filename (after filters to prevent human errors)!
|
77 |
-
return sanitize_file_name( $filename );
|
78 |
-
}
|
79 |
-
|
80 |
-
public function init_settings() {
|
81 |
-
// Register settings.
|
82 |
-
$page = $option_group = $option_name = 'wpo_wcpdf_documents_settings_packing-slip';
|
83 |
-
|
84 |
-
$settings_fields = array(
|
85 |
-
array(
|
86 |
-
'type' => 'section',
|
87 |
-
'id' => 'packing_slip',
|
88 |
-
'title' => '',
|
89 |
-
'callback' => 'section',
|
90 |
-
),
|
91 |
-
array(
|
92 |
-
'type' => 'setting',
|
93 |
-
'id' => 'enabled',
|
94 |
-
'title' => __( 'Enable', 'woocommerce-pdf-invoices-packing-slips' ),
|
95 |
-
'callback' => 'checkbox',
|
96 |
-
'section' => 'packing_slip',
|
97 |
-
'args' => array(
|
98 |
-
'option_name' => $option_name,
|
99 |
-
'id' => 'enabled',
|
100 |
-
)
|
101 |
-
),
|
102 |
-
array(
|
103 |
-
'type' => 'setting',
|
104 |
-
'id' => 'display_billing_address',
|
105 |
-
'title' => __( 'Display billing address', 'woocommerce-pdf-invoices-packing-slips' ),
|
106 |
-
'callback' => 'checkbox',
|
107 |
-
'section' => 'packing_slip',
|
108 |
-
'args' => array(
|
109 |
-
'option_name' => $option_name,
|
110 |
-
'id' => 'display_billing_address',
|
111 |
-
'description' => __( 'Display billing address (in addition to the default shipping address) if different from shipping address', 'woocommerce-pdf-invoices-packing-slips' ),
|
112 |
-
)
|
113 |
-
),
|
114 |
-
array(
|
115 |
-
'type' => 'setting',
|
116 |
-
'id' => 'display_email',
|
117 |
-
'title' => __( 'Display email address', 'woocommerce-pdf-invoices-packing-slips' ),
|
118 |
-
'callback' => 'checkbox',
|
119 |
-
'section' => 'packing_slip',
|
120 |
-
'args' => array(
|
121 |
-
'option_name' => $option_name,
|
122 |
-
'id' => 'display_email',
|
123 |
-
)
|
124 |
-
),
|
125 |
-
array(
|
126 |
-
'type' => 'setting',
|
127 |
-
'id' => 'display_phone',
|
128 |
-
'title' => __( 'Display phone number', 'woocommerce-pdf-invoices-packing-slips' ),
|
129 |
-
'callback' => 'checkbox',
|
130 |
-
'section' => 'packing_slip',
|
131 |
-
'args' => array(
|
132 |
-
'option_name' => $option_name,
|
133 |
-
'id' => 'display_phone',
|
134 |
-
)
|
135 |
-
),
|
136 |
-
);
|
137 |
-
|
138 |
-
|
139 |
-
// allow plugins to alter settings fields
|
140 |
-
$settings_fields = apply_filters( 'wpo_wcpdf_settings_fields_documents_packing_slip', $settings_fields, $page, $option_group, $option_name );
|
141 |
-
WPO_WCPDF()->settings->add_settings_fields( $settings_fields, $page, $option_group, $option_name );
|
142 |
-
return;
|
143 |
-
|
144 |
-
}
|
145 |
-
|
146 |
-
}
|
147 |
-
|
148 |
-
endif; // class_exists
|
149 |
-
|
150 |
return new Packing_Slip();
|
1 |
+
<?php
|
2 |
+
namespace WPO\WC\PDF_Invoices\Documents;
|
3 |
+
|
4 |
+
use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
|
5 |
+
use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
|
6 |
+
use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
|
7 |
+
|
8 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
9 |
+
exit; // Exit if accessed directly
|
10 |
+
}
|
11 |
+
|
12 |
+
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Documents\\Packing_Slip' ) ) :
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Packing Slip Document
|
16 |
+
*
|
17 |
+
* @class \WPO\WC\PDF_Invoices\Documents\Packing_Slip
|
18 |
+
* @version 2.0
|
19 |
+
* @category Class
|
20 |
+
* @author Ewout Fernhout
|
21 |
+
*/
|
22 |
+
|
23 |
+
class Packing_Slip extends Order_Document_Methods {
|
24 |
+
/**
|
25 |
+
* Init/load the order object.
|
26 |
+
*
|
27 |
+
* @param int|object|WC_Order $order Order to init.
|
28 |
+
*/
|
29 |
+
public function __construct( $order = 0 ) {
|
30 |
+
// set properties
|
31 |
+
$this->type = 'packing-slip';
|
32 |
+
$this->title = __( 'Packing Slip', 'woocommerce-pdf-invoices-packing-slips' );
|
33 |
+
$this->icon = WPO_WCPDF()->plugin_url() . "/assets/images/packing-slip.png";
|
34 |
+
|
35 |
+
// Call parent constructor
|
36 |
+
parent::__construct( $order );
|
37 |
+
}
|
38 |
+
|
39 |
+
public function get_title() {
|
40 |
+
// override/not using $this->title to allow for language switching!
|
41 |
+
return apply_filters( "wpo_wcpdf_{$this->slug}_title", __( 'Packing Slip', 'woocommerce-pdf-invoices-packing-slips' ), $this );
|
42 |
+
}
|
43 |
+
|
44 |
+
public function get_settings() {
|
45 |
+
$common_settings = WPO_WCPDF()->settings->get_common_document_settings();
|
46 |
+
$document_settings = get_option( 'wpo_wcpdf_documents_settings_packing-slip' );
|
47 |
+
return (array) $document_settings + (array) $common_settings;
|
48 |
+
}
|
49 |
+
|
50 |
+
public function get_filename( $context = 'download', $args = array() ) {
|
51 |
+
$order_count = isset($args['order_ids']) ? count($args['order_ids']) : 1;
|
52 |
+
|
53 |
+
$name = _n( 'packing-slip', 'packing-slips', $order_count, 'woocommerce-pdf-invoices-packing-slips' );
|
54 |
+
|
55 |
+
if ( $order_count == 1 ) {
|
56 |
+
if ( isset( $this->settings['display_number'] ) ) {
|
57 |
+
$suffix = (string) $this->get_number();
|
58 |
+
} else {
|
59 |
+
if ( empty( $this->order ) ) {
|
60 |
+
$order = WCX::get_order ( $order_ids[0] );
|
61 |
+
$suffix = method_exists( $order, 'get_order_number' ) ? $order->get_order_number() : '';
|
62 |
+
} else {
|
63 |
+
$suffix = method_exists( $this->order, 'get_order_number' ) ? $this->order->get_order_number() : '';
|
64 |
+
}
|
65 |
+
}
|
66 |
+
} else {
|
67 |
+
$suffix = date('Y-m-d'); // 2020-11-11
|
68 |
+
}
|
69 |
+
|
70 |
+
$filename = $name . '-' . $suffix . '.pdf';
|
71 |
+
|
72 |
+
// Filter filename
|
73 |
+
$order_ids = isset($args['order_ids']) ? $args['order_ids'] : array( $this->order_id );
|
74 |
+
$filename = apply_filters( 'wpo_wcpdf_filename', $filename, $this->get_type(), $order_ids, $context );
|
75 |
+
|
76 |
+
// sanitize filename (after filters to prevent human errors)!
|
77 |
+
return sanitize_file_name( $filename );
|
78 |
+
}
|
79 |
+
|
80 |
+
public function init_settings() {
|
81 |
+
// Register settings.
|
82 |
+
$page = $option_group = $option_name = 'wpo_wcpdf_documents_settings_packing-slip';
|
83 |
+
|
84 |
+
$settings_fields = array(
|
85 |
+
array(
|
86 |
+
'type' => 'section',
|
87 |
+
'id' => 'packing_slip',
|
88 |
+
'title' => '',
|
89 |
+
'callback' => 'section',
|
90 |
+
),
|
91 |
+
array(
|
92 |
+
'type' => 'setting',
|
93 |
+
'id' => 'enabled',
|
94 |
+
'title' => __( 'Enable', 'woocommerce-pdf-invoices-packing-slips' ),
|
95 |
+
'callback' => 'checkbox',
|
96 |
+
'section' => 'packing_slip',
|
97 |
+
'args' => array(
|
98 |
+
'option_name' => $option_name,
|
99 |
+
'id' => 'enabled',
|
100 |
+
)
|
101 |
+
),
|
102 |
+
array(
|
103 |
+
'type' => 'setting',
|
104 |
+
'id' => 'display_billing_address',
|
105 |
+
'title' => __( 'Display billing address', 'woocommerce-pdf-invoices-packing-slips' ),
|
106 |
+
'callback' => 'checkbox',
|
107 |
+
'section' => 'packing_slip',
|
108 |
+
'args' => array(
|
109 |
+
'option_name' => $option_name,
|
110 |
+
'id' => 'display_billing_address',
|
111 |
+
'description' => __( 'Display billing address (in addition to the default shipping address) if different from shipping address', 'woocommerce-pdf-invoices-packing-slips' ),
|
112 |
+
)
|
113 |
+
),
|
114 |
+
array(
|
115 |
+
'type' => 'setting',
|
116 |
+
'id' => 'display_email',
|
117 |
+
'title' => __( 'Display email address', 'woocommerce-pdf-invoices-packing-slips' ),
|
118 |
+
'callback' => 'checkbox',
|
119 |
+
'section' => 'packing_slip',
|
120 |
+
'args' => array(
|
121 |
+
'option_name' => $option_name,
|
122 |
+
'id' => 'display_email',
|
123 |
+
)
|
124 |
+
),
|
125 |
+
array(
|
126 |
+
'type' => 'setting',
|
127 |
+
'id' => 'display_phone',
|
128 |
+
'title' => __( 'Display phone number', 'woocommerce-pdf-invoices-packing-slips' ),
|
129 |
+
'callback' => 'checkbox',
|
130 |
+
'section' => 'packing_slip',
|
131 |
+
'args' => array(
|
132 |
+
'option_name' => $option_name,
|
133 |
+
'id' => 'display_phone',
|
134 |
+
)
|
135 |
+
),
|
136 |
+
);
|
137 |
+
|
138 |
+
|
139 |
+
// allow plugins to alter settings fields
|
140 |
+
$settings_fields = apply_filters( 'wpo_wcpdf_settings_fields_documents_packing_slip', $settings_fields, $page, $option_group, $option_name );
|
141 |
+
WPO_WCPDF()->settings->add_settings_fields( $settings_fields, $page, $option_group, $option_name );
|
142 |
+
return;
|
143 |
+
|
144 |
+
}
|
145 |
+
|
146 |
+
}
|
147 |
+
|
148 |
+
endif; // class_exists
|
149 |
+
|
150 |
return new Packing_Slip();
|
includes/documents/class-wcpdf-sequential-number-store.php
CHANGED
@@ -1,167 +1,167 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WPO\WC\PDF_Invoices\Documents;
|
3 |
-
|
4 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
5 |
-
exit; // Exit if accessed directly
|
6 |
-
}
|
7 |
-
|
8 |
-
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Documents\\Sequential_Number_Store' ) ) :
|
9 |
-
|
10 |
-
/**
|
11 |
-
* Class handling database interaction for sequential numbers
|
12 |
-
*
|
13 |
-
* @class \WPO\WC\PDF_Invoices\Documents\Sequential_Number_Store
|
14 |
-
* @version 2.0
|
15 |
-
* @category Class
|
16 |
-
* @author Ewout Fernhout
|
17 |
-
*/
|
18 |
-
|
19 |
-
class Sequential_Number_Store {
|
20 |
-
/**
|
21 |
-
* Name of the table that stores the number sequence (without the wp_wcpdf_ table prefix)
|
22 |
-
* @var String
|
23 |
-
*/
|
24 |
-
public $table_name;
|
25 |
-
|
26 |
-
/**
|
27 |
-
* Number store method, either 'auto_increment' or 'calculate'
|
28 |
-
* @var String
|
29 |
-
*/
|
30 |
-
public $method;
|
31 |
-
|
32 |
-
public function __construct( $table_name, $method = 'auto_increment' ) {
|
33 |
-
global $wpdb;
|
34 |
-
$this->table_name = "{$wpdb->prefix}wcpdf_{$table_name}"; // i.e. wp_wcpdf_invoice_number
|
35 |
-
$this->method = $method;
|
36 |
-
|
37 |
-
$this->init();
|
38 |
-
}
|
39 |
-
|
40 |
-
public function init() {
|
41 |
-
global $wpdb;
|
42 |
-
// check if table exists
|
43 |
-
if( $wpdb->get_var("SHOW TABLES LIKE '{$this->table_name}'") == $this->table_name) {
|
44 |
-
// check calculated_number column if using 'calculate' method
|
45 |
-
if ( $this->method == 'calculate' ) {
|
46 |
-
$column_exists = $wpdb->get_var("SHOW COLUMNS FROM `{$this->table_name}` LIKE 'calculated_number'");
|
47 |
-
if (empty($column_exists)) {
|
48 |
-
$wpdb->query("ALTER TABLE {$this->table_name} ADD calculated_number int (16)");
|
49 |
-
}
|
50 |
-
}
|
51 |
-
return; // no further business
|
52 |
-
}
|
53 |
-
|
54 |
-
// create table (in case of concurrent requests, this does no harm if it already exists)
|
55 |
-
$charset_collate = $wpdb->get_charset_collate();
|
56 |
-
// dbDelta is a sensitive kid, so we omit indentation
|
57 |
-
$sql = "CREATE TABLE {$this->table_name} (
|
58 |
-
id int(16) NOT NULL AUTO_INCREMENT,
|
59 |
-
order_id int(16),
|
60 |
-
date datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
|
61 |
-
calculated_number int (16),
|
62 |
-
PRIMARY KEY (id)
|
63 |
-
) $charset_collate;";
|
64 |
-
|
65 |
-
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
66 |
-
$result = dbDelta( $sql );
|
67 |
-
|
68 |
-
return $result;
|
69 |
-
}
|
70 |
-
|
71 |
-
/**
|
72 |
-
* Consume/create the next number and return it
|
73 |
-
* @param integer $order_id WooCommerce Order ID
|
74 |
-
* @param string $date Local date, formatted as Y-m-d H:i:s
|
75 |
-
* @return int Number that was consumed/created
|
76 |
-
*/
|
77 |
-
public function increment( $order_id = 0, $date = null ) {
|
78 |
-
global $wpdb;
|
79 |
-
if ( empty( $date ) ) {
|
80 |
-
$date = get_date_from_gmt( date( 'Y-m-d H:i:s' ) );
|
81 |
-
}
|
82 |
-
|
83 |
-
do_action( 'wpo_wcpdf_before_sequential_number_increment', $this, $order_id, $date );
|
84 |
-
|
85 |
-
$data = array(
|
86 |
-
'order_id' => (int) $order_id,
|
87 |
-
'date' => $date,
|
88 |
-
);
|
89 |
-
|
90 |
-
if ( $this->method == 'auto_increment' ) {
|
91 |
-
$wpdb->insert( $this->table_name, $data );
|
92 |
-
$number = $wpdb->insert_id;
|
93 |
-
} elseif ( $this->method == 'calculate' ) {
|
94 |
-
$number = $data['calculated_number'] = $this->get_next();
|
95 |
-
$wpdb->insert( $this->table_name, $data );
|
96 |
-
}
|
97 |
-
|
98 |
-
// return generated number
|
99 |
-
return $number;
|
100 |
-
}
|
101 |
-
|
102 |
-
/**
|
103 |
-
* Get the number that will be used on the next increment
|
104 |
-
* @return int next number
|
105 |
-
*/
|
106 |
-
public function get_next() {
|
107 |
-
global $wpdb;
|
108 |
-
if ( $this->method == 'auto_increment' ) {
|
109 |
-
// get next auto_increment value
|
110 |
-
$table_status = $wpdb->get_row("SHOW TABLE STATUS LIKE '{$this->table_name}'");
|
111 |
-
$next = $table_status->Auto_increment;
|
112 |
-
} elseif ( $this->method == 'calculate' ) {
|
113 |
-
$last_row = $wpdb->get_row( "SELECT * FROM {$this->table_name} WHERE id = ( SELECT MAX(id) from {$this->table_name} )" );
|
114 |
-
if ( empty( $last_row ) ) {
|
115 |
-
$next = 1;
|
116 |
-
} elseif ( !empty( $last_row->calculated_number ) ) {
|
117 |
-
$next = (int) $last_row->calculated_number + 1;
|
118 |
-
} else {
|
119 |
-
$next = (int) $last_row->id + 1;
|
120 |
-
}
|
121 |
-
}
|
122 |
-
return $next;
|
123 |
-
}
|
124 |
-
|
125 |
-
/**
|
126 |
-
* Set the number that will be used on the next increment
|
127 |
-
*/
|
128 |
-
public function set_next( $number = 1 ) {
|
129 |
-
global $wpdb;
|
130 |
-
|
131 |
-
// delete all rows
|
132 |
-
$delete = $wpdb->query("TRUNCATE TABLE {$this->table_name}");
|
133 |
-
|
134 |
-
// set auto_increment
|
135 |
-
if ( $number > 1 ) {
|
136 |
-
// if AUTO_INCREMENT is not 1, we need to make sure we have a 'highest value' in case of server restarts
|
137 |
-
// https://serverfault.com/questions/228690/mysql-auto-increment-fields-resets-by-itself
|
138 |
-
$highest_number = (int) $number - 1;
|
139 |
-
$wpdb->query("ALTER TABLE {$this->table_name} AUTO_INCREMENT={$highest_number};");
|
140 |
-
$data = array(
|
141 |
-
'order_id' => 0,
|
142 |
-
'date' => get_date_from_gmt( date( 'Y-m-d H:i:s' ) ),
|
143 |
-
);
|
144 |
-
|
145 |
-
if ( $this->method == 'calculate' ) {
|
146 |
-
$data['calculated_number'] = $highest_number;
|
147 |
-
}
|
148 |
-
|
149 |
-
// after this insert, AUTO_INCREMENT will be equal to $number
|
150 |
-
$wpdb->insert( $this->table_name, $data );
|
151 |
-
} else {
|
152 |
-
// simple scenario, no need to insert any rows
|
153 |
-
$wpdb->query("ALTER TABLE {$this->table_name} AUTO_INCREMENT={$number};");
|
154 |
-
}
|
155 |
-
}
|
156 |
-
|
157 |
-
public function get_last_date( $format = 'Y-m-d H:i:s' ) {
|
158 |
-
global $wpdb;
|
159 |
-
$row = $wpdb->get_row( "SELECT * FROM {$this->table_name} WHERE id = ( SELECT MAX(id) from {$this->table_name} )" );
|
160 |
-
$date = isset( $row->date ) ? $row->date : 'now';
|
161 |
-
$formatted_date = date( $format, strtotime( $date ) );
|
162 |
-
|
163 |
-
return $formatted_date;
|
164 |
-
}
|
165 |
-
}
|
166 |
-
|
167 |
-
endif; // class_exists
|
1 |
+
<?php
|
2 |
+
namespace WPO\WC\PDF_Invoices\Documents;
|
3 |
+
|
4 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
5 |
+
exit; // Exit if accessed directly
|
6 |
+
}
|
7 |
+
|
8 |
+
if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Documents\\Sequential_Number_Store' ) ) :
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Class handling database interaction for sequential numbers
|
12 |
+
*
|
13 |
+
* @class \WPO\WC\PDF_Invoices\Documents\Sequential_Number_Store
|
14 |
+
* @version 2.0
|
15 |
+
* @category Class
|
16 |
+
* @author Ewout Fernhout
|
17 |
+
*/
|
18 |
+
|
19 |
+
class Sequential_Number_Store {
|
20 |
+
/**
|
21 |
+
* Name of the table that stores the number sequence (without the wp_wcpdf_ table prefix)
|
22 |
+
* @var String
|
23 |
+
*/
|
24 |
+
public $table_name;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Number store method, either 'auto_increment' or 'calculate'
|
28 |
+
* @var String
|
29 |
+
*/
|
30 |
+
public $method;
|
31 |
+
|
32 |
+
public function __construct( $table_name, $method = 'auto_increment' ) {
|
33 |
+
global $wpdb;
|
34 |
+
$this->table_name = "{$wpdb->prefix}wcpdf_{$table_name}"; // i.e. wp_wcpdf_invoice_number
|
35 |
+
$this->method = $method;
|
36 |
+
|
37 |
+
$this->init();
|
38 |
+
}
|
39 |
+
|
40 |
+
public function init() {
|
41 |
+
global $wpdb;
|
42 |
+
// check if table exists
|
43 |
+
if( $wpdb->get_var("SHOW TABLES LIKE '{$this->table_name}'") == $this->table_name) {
|
44 |
+
// check calculated_number column if using 'calculate' method
|
45 |
+
if ( $this->method == 'calculate' ) {
|
46 |
+
$column_exists = $wpdb->get_var("SHOW COLUMNS FROM `{$this->table_name}` LIKE 'calculated_number'");
|
47 |
+
if (empty($column_exists)) {
|
48 |
+
$wpdb->query("ALTER TABLE {$this->table_name} ADD calculated_number int (16)");
|
49 |
+
}
|
50 |
+
}
|
51 |
+
return; // no further business
|
52 |
+
}
|
53 |
+
|
54 |
+
// create table (in case of concurrent requests, this does no harm if it already exists)
|
55 |
+
$charset_collate = $wpdb->get_charset_collate();
|
56 |
+
// dbDelta is a sensitive kid, so we omit indentation
|
57 |
+
$sql = "CREATE TABLE {$this->table_name} (
|
58 |
+
id int(16) NOT NULL AUTO_INCREMENT,
|
59 |
+
order_id int(16),
|
60 |
+
date datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
|
61 |
+
calculated_number int (16),
|
62 |
+
PRIMARY KEY (id)
|
63 |
+
) $charset_collate;";
|
64 |
+
|
65 |
+
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
66 |
+
$result = dbDelta( $sql );
|
67 |
+
|
68 |
+
return $result;
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Consume/create the next number and return it
|
73 |
+
* @param integer $order_id WooCommerce Order ID
|
74 |
+
* @param string $date Local date, formatted as Y-m-d H:i:s
|
75 |
+
* @return int Number that was consumed/created
|
76 |
+
*/
|
77 |
+
public function increment( $order_id = 0, $date = null ) {
|
78 |
+
global $wpdb;
|
79 |
+
if ( empty( $date ) ) {
|
80 |
+
$date = get_date_from_gmt( date( 'Y-m-d H:i:s' ) );
|
81 |
+
}
|
82 |
+
|
83 |
+
do_action( 'wpo_wcpdf_before_sequential_number_increment', $this, $order_id, $date );
|
84 |
+
|
85 |
+
$data = array(
|
86 |
+
'order_id' => (int) $order_id,
|
87 |
+
'date' => $date,
|
88 |
+
);
|
89 |
+
|
90 |
+
if ( $this->method == 'auto_increment' ) {
|
91 |
+
$wpdb->insert( $this->table_name, $data );
|
92 |
+
$number = $wpdb->insert_id;
|
93 |
+
} elseif ( $this->method == 'calculate' ) {
|
94 |
+
$number = $data['calculated_number'] = $this->get_next();
|
95 |
+
$wpdb->insert( $this->table_name, $data );
|
96 |
+
}
|
97 |
+
|
98 |
+
// return generated number
|
99 |
+
return $number;
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Get the number that will be used on the next increment
|
104 |
+
* @return int next number
|
105 |
+
*/
|
106 |
+
public function get_next() {
|
107 |
+
global $wpdb;
|
108 |
+
if ( $this->method == 'auto_increment' ) {
|
109 |
+
// get next auto_increment value
|
110 |
+
$table_status = $wpdb->get_row("SHOW TABLE STATUS LIKE '{$this->table_name}'");
|
111 |
+
$next = $table_status->Auto_increment;
|
112 |
+
} elseif ( $this->method == 'calculate' ) {
|
113 |
+
$last_row = $wpdb->get_row( "SELECT * FROM {$this->table_name} WHERE id = ( SELECT MAX(id) from {$this->table_name} )" );
|
114 |
+
if ( empty( $last_row ) ) {
|
115 |
+
$next = 1;
|
116 |
+
} elseif ( !empty( $last_row->calculated_number ) ) {
|
117 |
+
$next = (int) $last_row->calculated_number + 1;
|
118 |
+
} else {
|
119 |
+
$next = (int) $last_row->id + 1;
|
120 |
+
}
|
121 |
+
}
|
122 |
+
return $next;
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Set the number that will be used on the next increment
|
127 |
+
*/
|
128 |
+
public function set_next( $number = 1 ) {
|
129 |
+
global $wpdb;
|
130 |
+
|
131 |
+
// delete all rows
|
132 |
+
$delete = $wpdb->query("TRUNCATE TABLE {$this->table_name}");
|
133 |
+
|
134 |
+
// set auto_increment
|
135 |
+
if ( $number > 1 ) {
|
136 |
+
// if AUTO_INCREMENT is not 1, we need to make sure we have a 'highest value' in case of server restarts
|
137 |
+
// https://serverfault.com/questions/228690/mysql-auto-increment-fields-resets-by-itself
|
138 |
+
$highest_number = (int) $number - 1;
|
139 |
+
$wpdb->query("ALTER TABLE {$this->table_name} AUTO_INCREMENT={$highest_number};");
|
140 |
+
$data = array(
|
141 |
+
'order_id' => 0,
|
142 |
+
'date' => get_date_from_gmt( date( 'Y-m-d H:i:s' ) ),
|
143 |
+
);
|
144 |
+
|
145 |
+
if ( $this->method == 'calculate' ) {
|
146 |
+
$data['calculated_number'] = $highest_number;
|
147 |
+
}
|
148 |
+
|
149 |
+
// after this insert, AUTO_INCREMENT will be equal to $number
|
150 |
+
$wpdb->insert( $this->table_name, $data );
|
151 |
+
} else {
|
152 |
+
// simple scenario, no need to insert any rows
|
153 |
+
$wpdb->query("ALTER TABLE {$this->table_name} AUTO_INCREMENT={$number};");
|
154 |
+
}
|
155 |
+
}
|
156 |
+
|
157 |
+
public function get_last_date( $format = 'Y-m-d H:i:s' ) {
|
158 |
+
global $wpdb;
|
159 |
+
$row = $wpdb->get_row( "SELECT * FROM {$this->table_name} WHERE id = ( SELECT MAX(id) from {$this->table_name} )" );
|
160 |
+
$date = isset( $row->date ) ? $row->date : 'now';
|
161 |
+
$formatted_date = date( $format, strtotime( $date ) );
|
162 |
+
|
163 |
+
return $formatted_date;
|
164 |
+
}
|
165 |
+
}
|
166 |
+
|
167 |
+
endif; // class_exists
|
includes/views/attachment-settings-hint.php
CHANGED
@@ -1,23 +1,23 @@
|
|
1 |
-
<?php
|
2 |
-
$invoice_settings_url = add_query_arg( array(
|
3 |
-
'tab' => 'documents',
|
4 |
-
'section' => 'invoice',
|
5 |
-
) );
|
6 |
-
?>
|
7 |
-
<style>
|
8 |
-
.wcpdf-attachment-settings-hint {
|
9 |
-
display: inline-block;
|
10 |
-
background: #fff;
|
11 |
-
border-left: 4px solid #cc99c2 !important;
|
12 |
-
-webkit-box-shadow: 0 1px 1px 0 rgba( 0, 0, 0, 0.1 );
|
13 |
-
box-shadow: 0 1px 1px 0 rgba( 0, 0, 0, 0.1 );
|
14 |
-
padding: 15px;
|
15 |
-
margin-top: 15px;
|
16 |
-
font-size: 120%;
|
17 |
-
}
|
18 |
-
</style>
|
19 |
-
<!-- <div id="message" class="updated woocommerce-message"> -->
|
20 |
-
<div class="wcpdf-attachment-settings-hint">
|
21 |
-
<?php printf(__( "It looks like you haven't setup any email attachments yet, check the settings under <b>%sDocuments > Invoice%s</b>", 'woocommerce-pdf-invoices-packing-slips' ), '<a href="'.$invoice_settings_url.'">', '</a>'); ?><br>
|
22 |
-
<?php printf('<a href="%s" style="font">%s</a>', add_query_arg( 'wpo_wcpdf_hide_attachments_hint', 'true' ), __( 'Hide this message', 'woocommerce-pdf-invoices-packing-slips' ) ); ?>
|
23 |
-
</div>
|
1 |
+
<?php
|
2 |
+
$invoice_settings_url = add_query_arg( array(
|
3 |
+
'tab' => 'documents',
|
4 |
+
'section' => 'invoice',
|
5 |
+
) );
|
6 |
+
?>
|
7 |
+
<style>
|
8 |
+
.wcpdf-attachment-settings-hint {
|
9 |
+
display: inline-block;
|
10 |
+
background: #fff;
|
11 |
+
border-left: 4px solid #cc99c2 !important;
|
12 |
+
-webkit-box-shadow: 0 1px 1px 0 rgba( 0, 0, 0, 0.1 );
|
13 |
+
box-shadow: 0 1px 1px 0 rgba( 0, 0, 0, 0.1 );
|
14 |
+
padding: 15px;
|
15 |
+
margin-top: 15px;
|
16 |
+
font-size: 120%;
|
17 |
+
}
|
18 |
+
</style>
|
19 |
+
<!-- <div id="message" class="updated woocommerce-message"> -->
|
20 |
+
<div class="wcpdf-attachment-settings-hint">
|
21 |
+
<?php printf(__( "It looks like you haven't setup any email attachments yet, check the settings under <b>%sDocuments > Invoice%s</b>", 'woocommerce-pdf-invoices-packing-slips' ), '<a href="'.$invoice_settings_url.'">', '</a>'); ?><br>
|
22 |
+
<?php printf('<a href="%s" style="font">%s</a>', add_query_arg( 'wpo_wcpdf_hide_attachments_hint', 'true' ), __( 'Hide this message', 'woocommerce-pdf-invoices-packing-slips' ) ); ?>
|
23 |
+
</div>
|
includes/views/dompdf-status.php
CHANGED
@@ -1,203 +1,203 @@
|
|
1 |
-
<?php
|
2 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
3 |
-
exit; // Exit if accessed directly
|
4 |
-
}
|
5 |
-
|
6 |
-
$memory_limit = function_exists('wc_let_to_num')?wc_let_to_num( WP_MEMORY_LIMIT ):woocommerce_let_to_num( WP_MEMORY_LIMIT );
|
7 |
-
$php_mem_limit = function_exists( 'memory_get_usage' ) ? @ini_get( 'memory_limit' ) : '-';
|
8 |
-
|
9 |
-
$server_configs = array(
|
10 |
-
"DOMDocument extension" => array(
|
11 |
-
"required" => true,
|
12 |
-
"value" => phpversion("DOM"),
|
13 |
-
"result" => class_exists("DOMDocument"),
|
14 |
-
),
|
15 |
-
"MBString extension" => array(
|
16 |
-
"required" => true,
|
17 |
-
"value" => phpversion("mbstring"),
|
18 |
-
"result" => function_exists("mb_send_mail"),
|
19 |
-
"fallback" => "Recommended, will use fallback functions",
|
20 |
-
),
|
21 |
-
"GD" => array(
|
22 |
-
"required" => true,
|
23 |
-
"value" => phpversion("gd"),
|
24 |
-
"result" => function_exists("imagecreate"),
|
25 |
-
"fallback" => "Required if you have images in your documents",
|
26 |
-
),
|
27 |
-
// "PCRE" => array(
|
28 |
-
// "required" => true,
|
29 |
-
// "value" => phpversion("pcre"),
|
30 |
-
// "result" => function_exists("preg_match") && @preg_match("/./u", "a"),
|
31 |
-
// "failure" => "PCRE is required with Unicode support (the \"u\" modifier)",
|
32 |
-
// ),
|
33 |
-
"Zlib" => array(
|
34 |
-
"required" => "To compress PDF documents",
|
35 |
-
"value" => phpversion("zlib"),
|
36 |
-
"result" => function_exists("gzcompress"),
|
37 |
-
"fallback" => "Recommended to compress PDF documents",
|
38 |
-
),
|
39 |
-
"opcache" => array(
|
40 |
-
"required" => "For better performances",
|
41 |
-
"value" => null,
|
42 |
-
"result" => false,
|
43 |
-
"fallback" => "Recommended for better performances",
|
44 |
-
),
|
45 |
-
"GMagick or IMagick" => array(
|
46 |
-
"required" => "Better with transparent PNG images",
|
47 |
-
"value" => null,
|
48 |
-
"result" => extension_loaded("gmagick") || extension_loaded("imagick"),
|
49 |
-
"fallback" => "Recommended for better performances",
|
50 |
-
),
|
51 |
-
"glob()" => array(
|
52 |
-
"required" => "Required to detect custom templates and to clear the temp folder periodically",
|
53 |
-
"value" => null,
|
54 |
-
"result" => function_exists("glob"),
|
55 |
-
"fallback" => "Check php disable_functions",
|
56 |
-
),
|
57 |
-
"WP Memory Limit" => array(
|
58 |
-
"required" => 'Recommended: 128MB (more for plugin-heavy setups)<br/>See: <a href="http://codex.wordpress.org/Editing_wp-config.php#Increasing_memory_allocated_to_PHP">Increasing memory allocated to PHP</a>',
|
59 |
-
"value" => sprintf("WordPress: %s, PHP: %s", WP_MEMORY_LIMIT, $php_mem_limit ),
|
60 |
-
"result" => $memory_limit > 67108864,
|
61 |
-
),
|
62 |
-
'allow_url_fopen' => array (
|
63 |
-
'required' => 'Allow remote stylesheets and images',
|
64 |
-
'value' => null,
|
65 |
-
'result' => ini_get("allow_url_fopen"),
|
66 |
-
"fallback" => "allow_url_fopen disabled",
|
67 |
-
),
|
68 |
-
);
|
69 |
-
|
70 |
-
if (($xc = extension_loaded("xcache")) || ($apc = extension_loaded("apc")) || ($zop = extension_loaded("Zend OPcache")) || ($op = extension_loaded("opcache"))) {
|
71 |
-
$server_configs["opcache"]["result"] = true;
|
72 |
-
$server_configs["opcache"]["value"] = (
|
73 |
-
$xc ? "XCache ".phpversion("xcache") : (
|
74 |
-
$apc ? "APC ".phpversion("apc") : (
|
75 |
-
$zop ? "Zend OPCache ".phpversion("Zend OPcache") : "PHP OPCache ".phpversion("opcache")
|
76 |
-
)
|
77 |
-
)
|
78 |
-
);
|
79 |
-
}
|
80 |
-
if (($gm = extension_loaded("gmagick")) || ($im = extension_loaded("imagick"))) {
|
81 |
-
$server_configs["GMagick or IMagick"]["value"] = ($im ? "IMagick ".phpversion("imagick") : "GMagick ".phpversion("gmagick"));
|
82 |
-
}
|
83 |
-
|
84 |
-
?>
|
85 |
-
|
86 |
-
<h3 id="system">System Configuration</h3>
|
87 |
-
|
88 |
-
<table cellspacing="1px" cellpadding="4px" style="background-color: white; padding: 5px; border: 1px solid #ccc;">
|
89 |
-
<tr>
|
90 |
-
<th align="left"> </th>
|
91 |
-
<th align="left">Required</th>
|
92 |
-
<th align="left">Present</th>
|
93 |
-
</tr>
|
94 |
-
|
95 |
-
<?php foreach($server_configs as $label => $server_config) {
|
96 |
-
if ($server_config["result"]) {
|
97 |
-
$background = "#9e4";
|
98 |
-
$color = "black";
|
99 |
-
} elseif (isset($server_config["fallback"])) {
|
100 |
-
$background = "#FCC612";
|
101 |
-
$color = "black";
|
102 |
-
} else {
|
103 |
-
$background = "#f43";
|
104 |
-
$color = "white";
|
105 |
-
}
|
106 |
-
?>
|
107 |
-
<tr>
|
108 |
-
<td class="title"><?php echo $label; ?></td>
|
109 |
-
<td><?php echo ($server_config["required"] === true ? "Yes" : $server_config["required"]); ?></td>
|
110 |
-
<td style="background-color:<?php echo $background; ?>; color:<?php echo $color; ?>">
|
111 |
-
<?php
|
112 |
-
echo $server_config["value"];
|
113 |
-
if ($server_config["result"] && !$server_config["value"]) echo "Yes";
|
114 |
-
if (!$server_config["result"]) {
|
115 |
-
if (isset($server_config["fallback"])) {
|
116 |
-
echo "<div>No. ".$server_config["fallback"]."</div>";
|
117 |
-
}
|
118 |
-
if (isset($server_config["failure"])) {
|
119 |
-
echo "<div>".$server_config["failure"]."</div>";
|
120 |
-
}
|
121 |
-
}
|
122 |
-
?>
|
123 |
-
</td>
|
124 |
-
</tr>
|
125 |
-
<?php } ?>
|
126 |
-
|
127 |
-
</table>
|
128 |
-
|
129 |
-
<?php
|
130 |
-
$permissions = array(
|
131 |
-
'WCPDF_TEMP_DIR' => array (
|
132 |
-
'description' => 'Central temporary plugin folder',
|
133 |
-
'value' => WPO_WCPDF()->main->get_tmp_path(),
|
134 |
-
'status' => (is_writable( WPO_WCPDF()->main->get_tmp_path() ) ? "ok" : "failed"),
|
135 |
-
'status_message' => (is_writable( WPO_WCPDF()->main->get_tmp_path() ) ? "Writable" : "Not writable"),
|
136 |
-
),
|
137 |
-
'WCPDF_ATTACHMENT_DIR' => array (
|
138 |
-
'description' => 'Temporary attachments folder',
|
139 |
-
'value' => trailingslashit( WPO_WCPDF()->main->get_tmp_path( 'attachments' ) ),
|
140 |
-
'status' => (is_writable( WPO_WCPDF()->main->get_tmp_path( 'attachments' ) ) ? "ok" : "failed"),
|
141 |
-
'status_message' => (is_writable( WPO_WCPDF()->main->get_tmp_path( 'attachments' ) ) ? "Writable" : "Not writable"),
|
142 |
-
),
|
143 |
-
'DOMPDF_TEMP_DIR' => array (
|
144 |
-
'description' => 'Temporary DOMPDF folder',
|
145 |
-
'value' => trailingslashit(WPO_WCPDF()->main->get_tmp_path( 'dompdf' )),
|
146 |
-
'status' => (is_writable(WPO_WCPDF()->main->get_tmp_path( 'dompdf' )) ? "ok" : "failed"),
|
147 |
-
'status_message' => (is_writable(WPO_WCPDF()->main->get_tmp_path( 'dompdf' )) ? "Writable" : "Not writable"),
|
148 |
-
),
|
149 |
-
'DOMPDF_FONT_DIR' => array (
|
150 |
-
'description' => 'DOMPDF fonts folder (needs to be writable for custom/remote fonts)',
|
151 |
-
'value' => trailingslashit(WPO_WCPDF()->main->get_tmp_path( 'fonts' )),
|
152 |
-
'status' => (is_writable(WPO_WCPDF()->main->get_tmp_path( 'fonts' )) ? "ok" : "failed"),
|
153 |
-
'status_message' => (is_writable(WPO_WCPDF()->main->get_tmp_path( 'fonts' )) ? "Writable" : "Not writable"),
|
154 |
-
),
|
155 |
-
);
|
156 |
-
|
157 |
-
$upload_dir = wp_upload_dir();
|
158 |
-
$upload_base = trailingslashit( $upload_dir['basedir'] );
|
159 |
-
|
160 |
-
?>
|
161 |
-
<br />
|
162 |
-
<h3 id="system">Write Permissions</h3>
|
163 |
-
<table cellspacing="1px" cellpadding="4px" style="background-color: white; padding: 5px; border: 1px solid #ccc;">
|
164 |
-
<tr>
|
165 |
-
<th align="left">Description</th>
|
166 |
-
<th align="left">Value</th>
|
167 |
-
<th align="left">Status</th>
|
168 |
-
</tr>
|
169 |
-
<?php
|
170 |
-
foreach ($permissions as $permission) {
|
171 |
-
if ($permission['status'] == 'ok') {
|
172 |
-
$background = "#9e4";
|
173 |
-
$color = "black";
|
174 |
-
} else {
|
175 |
-
$background = "#f43";
|
176 |
-
$color = "white";
|
177 |
-
}
|
178 |
-
?>
|
179 |
-
<tr>
|
180 |
-
<td><?php echo $permission['description']; ?></td>
|
181 |
-
<td><?php echo $permission['value']; ?></td>
|
182 |
-
<td style="background-color:<?php echo $background; ?>; color:<?php echo $color; ?>"><?php echo $permission['status_message']; ?></td>
|
183 |
-
</tr>
|
184 |
-
|
185 |
-
<?php } ?>
|
186 |
-
|
187 |
-
</table>
|
188 |
-
|
189 |
-
<p>
|
190 |
-
The central temp folder is <code><?php echo WPO_WCPDF()->main->get_tmp_path(); ?></code>.
|
191 |
-
By default, this folder is created in the WordPress uploads folder (<code><?php echo $upload_base; ?></code>),
|
192 |
-
which can be defined by setting <code>UPLOADS</code> in wp-config.php.
|
193 |
-
Alternatively, you can control the specific folder for PDF invoices by using the
|
194 |
-
<code>wpo_wcpdf_tmp_path</code> filter. Make sure this folder is writable and that the
|
195 |
-
subfolders <code>attachments</code>, <code>dompdf</code> and <code>fonts</code>
|
196 |
-
are present (these will be created by the plugin if the central temp folder is writable).<br>
|
197 |
-
<br>
|
198 |
-
If the temporary folders were not automatically created by the plugin, verify that all the font
|
199 |
-
files (from <code><?php echo WPO_WCPDF()->plugin_path() . "/vendor/dompdf/dompdf/lib/fonts/"; ?></code>)
|
200 |
-
are copied to the fonts folder.
|
201 |
-
Normally, this is fully automated, but if your server has strict security settings, this automated
|
202 |
-
copying may have been prohibited. In that case, you also need to make sure these folders get
|
203 |
synchronized on plugin updates!
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
3 |
+
exit; // Exit if accessed directly
|
4 |
+
}
|
5 |
+
|
6 |
+
$memory_limit = function_exists('wc_let_to_num')?wc_let_to_num( WP_MEMORY_LIMIT ):woocommerce_let_to_num( WP_MEMORY_LIMIT );
|
7 |
+
$php_mem_limit = function_exists( 'memory_get_usage' ) ? @ini_get( 'memory_limit' ) : '-';
|
8 |
+
|
9 |
+
$server_configs = array(
|
10 |
+
"DOMDocument extension" => array(
|
11 |
+
"required" => true,
|
12 |
+
"value" => phpversion("DOM"),
|
13 |
+
"result" => class_exists("DOMDocument"),
|
14 |
+
),
|
15 |
+
"MBString extension" => array(
|
16 |
+
"required" => true,
|
17 |
+
"value" => phpversion("mbstring"),
|
18 |
+
"result" => function_exists("mb_send_mail"),
|
19 |
+
"fallback" => "Recommended, will use fallback functions",
|
20 |
+
),
|
21 |
+
"GD" => array(
|
22 |
+
"required" => true,
|
23 |
+
"value" => phpversion("gd"),
|
24 |
+
"result" => function_exists("imagecreate"),
|
25 |
+
"fallback" => "Required if you have images in your documents",
|
26 |
+
),
|
27 |
+
// "PCRE" => array(
|
28 |
+
// "required" => true,
|
29 |
+
// "value" => phpversion("pcre"),
|
30 |
+
// "result" => function_exists("preg_match") && @preg_match("/./u", "a"),
|
31 |
+
// "failure" => "PCRE is required with Unicode support (the \"u\" modifier)",
|
32 |
+
// ),
|
33 |
+
"Zlib" => array(
|
34 |
+
"required" => "To compress PDF documents",
|
35 |
+
"value" => phpversion("zlib"),
|
36 |
+
"result" => function_exists("gzcompress"),
|
37 |
+
"fallback" => "Recommended to compress PDF documents",
|
38 |
+
),
|
39 |
+
"opcache" => array(
|
40 |
+
"required" => "For better performances",
|
41 |
+
"value" => null,
|
42 |
+
"result" => false,
|
43 |
+
"fallback" => "Recommended for better performances",
|
44 |
+
),
|
45 |
+
"GMagick or IMagick" => array(
|
46 |
+
"required" => "Better with transparent PNG images",
|
47 |
+
"value" => null,
|
48 |
+
"result" => extension_loaded("gmagick") || extension_loaded("imagick"),
|
49 |
+
"fallback" => "Recommended for better performances",
|
50 |
+
),
|
51 |
+
"glob()" => array(
|
52 |
+
"required" => "Required to detect custom templates and to clear the temp folder periodically",
|
53 |
+
"value" => null,
|
54 |
+
"result" => function_exists("glob"),
|
55 |
+
"fallback" => "Check php disable_functions",
|
56 |
+
),
|
57 |
+
"WP Memory Limit" => array(
|
58 |
+
"required" => 'Recommended: 128MB (more for plugin-heavy setups)<br/>See: <a href="http://codex.wordpress.org/Editing_wp-config.php#Increasing_memory_allocated_to_PHP">Increasing memory allocated to PHP</a>',
|
59 |
+
"value" => sprintf("WordPress: %s, PHP: %s", WP_MEMORY_LIMIT, $php_mem_limit ),
|
60 |
+
"result" => $memory_limit > 67108864,
|
61 |
+
),
|
62 |
+
'allow_url_fopen' => array (
|
63 |
+
'required' => 'Allow remote stylesheets and images',
|
64 |
+
'value' => null,
|
65 |
+
'result' => ini_get("allow_url_fopen"),
|
66 |
+
"fallback" => "allow_url_fopen disabled",
|
67 |
+
),
|
68 |
+
);
|
69 |
+
|
70 |
+
if (($xc = extension_loaded("xcache")) || ($apc = extension_loaded("apc")) || ($zop = extension_loaded("Zend OPcache")) || ($op = extension_loaded("opcache"))) {
|
71 |
+
$server_configs["opcache"]["result"] = true;
|
72 |
+
$server_configs["opcache"]["value"] = (
|
73 |
+
$xc ? "XCache ".phpversion("xcache") : (
|
74 |
+
$apc ? "APC ".phpversion("apc") : (
|
75 |
+
$zop ? "Zend OPCache ".phpversion("Zend OPcache") : "PHP OPCache ".phpversion("opcache")
|
76 |
+
)
|
77 |
+
)
|
78 |
+
);
|
79 |
+
}
|
80 |
+
if (($gm = extension_loaded("gmagick")) || ($im = extension_loaded("imagick"))) {
|
81 |
+
$server_configs["GMagick or IMagick"]["value"] = ($im ? "IMagick ".phpversion("imagick") : "GMagick ".phpversion("gmagick"));
|
82 |
+
}
|
83 |
+
|
84 |
+
?>
|
85 |
+
|
86 |
+
<h3 id="system">System Configuration</h3>
|
87 |
+
|
88 |
+
<table cellspacing="1px" cellpadding="4px" style="background-color: white; padding: 5px; border: 1px solid #ccc;">
|
89 |
+
<tr>
|
90 |
+
<th align="left"> </th>
|
91 |
+
<th align="left">Required</th>
|
92 |
+
<th align="left">Present</th>
|
93 |
+
</tr>
|
94 |
+
|
95 |
+
<?php foreach($server_configs as $label => $server_config) {
|
96 |
+
if ($server_config["result"]) {
|
97 |
+
$background = "#9e4";
|
98 |
+
$color = "black";
|
99 |
+
} elseif (isset($server_config["fallback"])) {
|
100 |
+
$background = "#FCC612";
|
101 |
+
$color = "black";
|
102 |
+
} else {
|
103 |
+
$background = "#f43";
|
104 |
+
$color = "white";
|
105 |
+
}
|
106 |
+
?>
|
107 |
+
<tr>
|
108 |
+
<td class="title"><?php echo $label; ?></td>
|
109 |
+
<td><?php echo ($server_config["required"] === true ? "Yes" : $server_config["required"]); ?></td>
|
110 |
+
<td style="background-color:<?php echo $background; ?>; color:<?php echo $color; ?>">
|
111 |
+
<?php
|
112 |
+
echo $server_config["value"];
|
113 |
+
if ($server_config["result"] && !$server_config["value"]) echo "Yes";
|
114 |
+
if (!$server_config["result"]) {
|
115 |
+
if (isset($server_config["fallback"])) {
|
116 |
+
echo "<div>No. ".$server_config["fallback"]."</div>";
|
117 |
+
}
|
118 |
+
if (isset($server_config["failure"])) {
|
119 |
+
echo "<div>".$server_config["failure"]."</div>";
|
120 |
+
}
|
121 |
+
}
|
122 |
+
?>
|
123 |
+
</td>
|
124 |
+
</tr>
|
125 |
+
<?php } ?>
|
126 |
+
|
127 |
+
</table>
|
128 |
+
|
129 |
+
<?php
|
130 |
+
$permissions = array(
|
131 |
+
'WCPDF_TEMP_DIR' => array (
|
132 |
+
'description' => 'Central temporary plugin folder',
|
133 |
+
'value' => WPO_WCPDF()->main->get_tmp_path(),
|
134 |
+
'status' => (is_writable( WPO_WCPDF()->main->get_tmp_path() ) ? "ok" : "failed"),
|
135 |
+
'status_message' => (is_writable( WPO_WCPDF()->main->get_tmp_path() ) ? "Writable" : "Not writable"),
|
136 |
+
),
|
137 |
+
'WCPDF_ATTACHMENT_DIR' => array (
|
138 |
+
'description' => 'Temporary attachments folder',
|
139 |
+
'value' => trailingslashit( WPO_WCPDF()->main->get_tmp_path( 'attachments' ) ),
|
140 |
+
'status' => (is_writable( WPO_WCPDF()->main->get_tmp_path( 'attachments' ) ) ? "ok" : "failed"),
|
141 |
+
'status_message' => (is_writable( WPO_WCPDF()->main->get_tmp_path( 'attachments' ) ) ? "Writable" : "Not writable"),
|
142 |
+
),
|
143 |
+
'DOMPDF_TEMP_DIR' => array (
|
144 |
+
'description' => 'Temporary DOMPDF folder',
|
145 |
+
'value' => trailingslashit(WPO_WCPDF()->main->get_tmp_path( 'dompdf' )),
|
146 |
+
'status' => (is_writable(WPO_WCPDF()->main->get_tmp_path( 'dompdf' )) ? "ok" : "failed"),
|
147 |
+
'status_message' => (is_writable(WPO_WCPDF()->main->get_tmp_path( 'dompdf' )) ? "Writable" : "Not writable"),
|
148 |
+
),
|
149 |
+
'DOMPDF_FONT_DIR' => array (
|
150 |
+
'description' => 'DOMPDF fonts folder (needs to be writable for custom/remote fonts)',
|
151 |
+
'value' => trailingslashit(WPO_WCPDF()->main->get_tmp_path( 'fonts' )),
|
152 |
+
'status' => (is_writable(WPO_WCPDF()->main->get_tmp_path( 'fonts' )) ? "ok" : "failed"),
|
153 |
+
'status_message' => (is_writable(WPO_WCPDF()->main->get_tmp_path( 'fonts' )) ? "Writable" : "Not writable"),
|
154 |
+
),
|
155 |
+
);
|
156 |
+
|
157 |
+
$upload_dir = wp_upload_dir();
|
158 |
+
$upload_base = trailingslashit( $upload_dir['basedir'] );
|
159 |
+
|
160 |
+
?>
|
161 |
+
<br />
|
162 |
+
<h3 id="system">Write Permissions</h3>
|
163 |
+
<table cellspacing="1px" cellpadding="4px" style="background-color: white; padding: 5px; border: 1px solid #ccc;">
|
164 |
+
<tr>
|
165 |
+
<th align="left">Description</th>
|
166 |
+
<th align="left">Value</th>
|
167 |
+
<th align="left">Status</th>
|
168 |
+
</tr>
|
169 |
+
<?php
|
170 |
+
foreach ($permissions as $permission) {
|
171 |
+
if ($permission['status'] == 'ok') {
|
172 |
+
$background = "#9e4";
|
173 |
+
$color = "black";
|
174 |
+
} else {
|
175 |
+
$background = "#f43";
|
176 |
+
$color = "white";
|
177 |
+
}
|
178 |
+
?>
|
179 |
+
<tr>
|
180 |
+
<td><?php echo $permission['description']; ?></td>
|
181 |
+
<td><?php echo $permission['value']; ?></td>
|
182 |
+
<td style="background-color:<?php echo $background; ?>; color:<?php echo $color; ?>"><?php echo $permission['status_message']; ?></td>
|
183 |
+
</tr>
|
184 |
+
|
185 |
+
<?php } ?>
|
186 |
+
|
187 |
+
</table>
|
188 |
+
|
189 |
+
<p>
|
190 |
+
The central temp folder is <code><?php echo WPO_WCPDF()->main->get_tmp_path(); ?></code>.
|
191 |
+
By default, this folder is created in the WordPress uploads folder (<code><?php echo $upload_base; ?></code>),
|
192 |
+
which can be defined by setting <code>UPLOADS</code> in wp-config.php.
|
193 |
+
Alternatively, you can control the specific folder for PDF invoices by using the
|
194 |
+
<code>wpo_wcpdf_tmp_path</code> filter. Make sure this folder is writable and that the
|
195 |
+
subfolders <code>attachments</code>, <code>dompdf</code> and <code>fonts</code>
|
196 |
+
are present (these will be created by the plugin if the central temp folder is writable).<br>
|
197 |
+
<br>
|
198 |
+
If the temporary folders were not automatically created by the plugin, verify that all the font
|
199 |
+
files (from <code><?php echo WPO_WCPDF()->plugin_path() . "/vendor/dompdf/dompdf/lib/fonts/"; ?></code>)
|
200 |
+
are copied to the fonts folder.
|
201 |
+
Normally, this is fully automated, but if your server has strict security settings, this automated
|
202 |
+
copying may have been prohibited. In that case, you also need to make sure these folders get
|
203 |
synchronized on plugin updates!
|
includes/views/setup-wizard/attach-to.php
CHANGED
@@ -1,23 +1,23 @@
|
|
1 |
-
<div class="wpo-step-description">
|
2 |
-
<h2><?php _e( 'Attach too...', 'woocommerce-pdf-invoices-packing-slips' ); ?></h2>
|
3 |
-
<p><?php _e( 'Select to which emails you would like to attach your invoice.', 'woocommerce-pdf-invoices-packing-slips' ); ?></p>
|
4 |
-
</div>
|
5 |
-
<div class="wpo-setup-input">
|
6 |
-
<?php
|
7 |
-
$current_settings = get_option( 'wpo_wcpdf_documents_settings_invoice', array() );
|
8 |
-
// echo "<pre>".var_dump($current_settings)."</pre>";
|
9 |
-
// load invoice to reuse method to get wc emails
|
10 |
-
$invoice = wcpdf_get_invoice( null );
|
11 |
-
$wc_emails = $invoice->get_wc_emails();
|
12 |
-
foreach ($wc_emails as $email_id => $name) {
|
13 |
-
if (!empty($current_settings['attach_to_email_ids'][$email_id])) {
|
14 |
-
$checked = 'checked';
|
15 |
-
} else {
|
16 |
-
$checked = '';
|
17 |
-
}
|
18 |
-
printf('<input type="hidden" value="" name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][attach_to_email_ids][%1$s]">
|
19 |
-
<input type="checkbox" %3$s name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][attach_to_email_ids][%1$s]" value="1">
|
20 |
-
<span class="checkbox">%2$s</span><br>', $email_id, $name, $checked);
|
21 |
-
}
|
22 |
-
?>
|
23 |
-
</div>
|
1 |
+
<div class="wpo-step-description">
|
2 |
+
<h2><?php _e( 'Attach too...', 'woocommerce-pdf-invoices-packing-slips' ); ?></h2>
|
3 |
+
<p><?php _e( 'Select to which emails you would like to attach your invoice.', 'woocommerce-pdf-invoices-packing-slips' ); ?></p>
|
4 |
+
</div>
|
5 |
+
<div class="wpo-setup-input">
|
6 |
+
<?php
|
7 |
+
$current_settings = get_option( 'wpo_wcpdf_documents_settings_invoice', array() );
|
8 |
+
// echo "<pre>".var_dump($current_settings)."</pre>";
|
9 |
+
// load invoice to reuse method to get wc emails
|
10 |
+
$invoice = wcpdf_get_invoice( null );
|
11 |
+
$wc_emails = $invoice->get_wc_emails();
|
12 |
+
foreach ($wc_emails as $email_id => $name) {
|
13 |
+
if (!empty($current_settings['attach_to_email_ids'][$email_id])) {
|
14 |
+
$checked = 'checked';
|
15 |
+
} else {
|
16 |
+
$checked = '';
|
17 |
+
}
|
18 |
+
printf('<input type="hidden" value="" name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][attach_to_email_ids][%1$s]">
|
19 |
+
<input type="checkbox" %3$s name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][attach_to_email_ids][%1$s]" value="1">
|
20 |
+
<span class="checkbox">%2$s</span><br>', $email_id, $name, $checked);
|
21 |
+
}
|
22 |
+
?>
|
23 |
+
</div>
|
includes/views/setup-wizard/display-options.php
CHANGED
@@ -1,19 +1,19 @@
|
|
1 |
-
<div class="wpo-step-description">
|
2 |
-
<h2><?php _e( 'Display options', 'woocommerce-pdf-invoices-packing-slips' ); ?></h2>
|
3 |
-
<p><?php _e( 'Select some additional display options for your invoice.', 'woocommerce-pdf-invoices-packing-slips' ); ?></p>
|
4 |
-
</div>
|
5 |
-
<div class="wpo-setup-input">
|
6 |
-
<?php
|
7 |
-
$current_settings = get_option( 'wpo_wcpdf_documents_settings_invoice', array() );
|
8 |
-
?>
|
9 |
-
<input type="hidden" name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_shipping_address]" value="">
|
10 |
-
<input type="checkbox" <?php echo !empty($current_settings['display_shipping_address']) ? 'checked' : ''; ?> name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_shipping_address]" value="1"><span class="checkbox"><?php _e( 'Display shipping address', 'woocommerce-pdf-invoices-packing-slips' ); ?></span><br>
|
11 |
-
<input type="hidden" name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_email]" value="">
|
12 |
-
<input type="checkbox" <?php echo !empty($current_settings['display_email']) ? 'checked' : ''; ?> name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_email]" value="1"><span class="checkbox"><?php _e( 'Display email address', 'woocommerce-pdf-invoices-packing-slips' ); ?></span><br>
|
13 |
-
<input type="hidden" name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_phone]" value="">
|
14 |
-
<input type="checkbox" <?php echo !empty($current_settings['display_phone']) ? 'checked' : ''; ?> name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_phone]" value="1"><span class="checkbox"><?php _e( 'Display phone number', 'woocommerce-pdf-invoices-packing-slips' ); ?></span><br>
|
15 |
-
<input type="hidden" name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_date]" value="">
|
16 |
-
<input type="checkbox" <?php echo !empty($current_settings['display_date']) ? 'checked' : ''; ?> name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_date]" value="invoice_date"><span class="checkbox"><?php _e( 'Display invoice date', 'woocommerce-pdf-invoices-packing-slips' ); ?></span><br>
|
17 |
-
<input type="hidden" name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_number]" value="">
|
18 |
-
<input type="checkbox" <?php echo !empty($current_settings['display_number']) ? 'checked' : ''; ?> name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_number]" value="invoice_number"><span class="checkbox"><?php _e( 'Display invoice number', 'woocommerce-pdf-invoices-packing-slips' ); ?></span><br>
|
19 |
</div>
|
1 |
+
<div class="wpo-step-description">
|
2 |
+
<h2><?php _e( 'Display options', 'woocommerce-pdf-invoices-packing-slips' ); ?></h2>
|
3 |
+
<p><?php _e( 'Select some additional display options for your invoice.', 'woocommerce-pdf-invoices-packing-slips' ); ?></p>
|
4 |
+
</div>
|
5 |
+
<div class="wpo-setup-input">
|
6 |
+
<?php
|
7 |
+
$current_settings = get_option( 'wpo_wcpdf_documents_settings_invoice', array() );
|
8 |
+
?>
|
9 |
+
<input type="hidden" name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_shipping_address]" value="">
|
10 |
+
<input type="checkbox" <?php echo !empty($current_settings['display_shipping_address']) ? 'checked' : ''; ?> name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_shipping_address]" value="1"><span class="checkbox"><?php _e( 'Display shipping address', 'woocommerce-pdf-invoices-packing-slips' ); ?></span><br>
|
11 |
+
<input type="hidden" name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_email]" value="">
|
12 |
+
<input type="checkbox" <?php echo !empty($current_settings['display_email']) ? 'checked' : ''; ?> name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_email]" value="1"><span class="checkbox"><?php _e( 'Display email address', 'woocommerce-pdf-invoices-packing-slips' ); ?></span><br>
|
13 |
+
<input type="hidden" name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_phone]" value="">
|
14 |
+
<input type="checkbox" <?php echo !empty($current_settings['display_phone']) ? 'checked' : ''; ?> name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_phone]" value="1"><span class="checkbox"><?php _e( 'Display phone number', 'woocommerce-pdf-invoices-packing-slips' ); ?></span><br>
|
15 |
+
<input type="hidden" name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_date]" value="">
|
16 |
+
<input type="checkbox" <?php echo !empty($current_settings['display_date']) ? 'checked' : ''; ?> name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_date]" value="invoice_date"><span class="checkbox"><?php _e( 'Display invoice date', 'woocommerce-pdf-invoices-packing-slips' ); ?></span><br>
|
17 |
+
<input type="hidden" name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_number]" value="">
|
18 |
+
<input type="checkbox" <?php echo !empty($current_settings['display_number']) ? 'checked' : ''; ?> name="wcpdf_settings[wpo_wcpdf_documents_settings_invoice][display_number]" value="invoice_number"><span class="checkbox"><?php _e( 'Display invoice number', 'woocommerce-pdf-invoices-packing-slips' ); ?></span><br>
|
19 |
</div>
|
includes/views/setup-wizard/good-to-go.php
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
-
<div class="wpo-step-description wpo-final">
|
2 |
-
<h1><?php _e( 'You are good to go!' , 'woocommerce-pdf-invoices-packing-slips' ); ?></h1>
|
3 |
-
<p><?php _e( 'If you have any questions please have a look at our documentation:', 'woocommerce-pdf-invoices-packing-slips' ); ?><br>
|
4 |
-
<a href="https://docs.wpovernight.com/category/woocommerce-pdf-invoices-packing-slips/" target="_blank"><?php _e( 'Invoices & Packing Slips' , 'woocommerce-pdf-invoices-packing-slips' ); ?></a></p>
|
5 |
-
<h2><?php _e( 'Happy selling!' , 'woocommerce-pdf-invoices-packing-slips' ); ?></h2>
|
6 |
</div>
|
1 |
+
<div class="wpo-step-description wpo-final">
|
2 |
+
<h1><?php _e( 'You are good to go!' , 'woocommerce-pdf-invoices-packing-slips' ); ?></h1>
|
3 |
+
<p><?php _e( 'If you have any questions please have a look at our documentation:', 'woocommerce-pdf-invoices-packing-slips' ); ?><br>
|
4 |
+
<a href="https://docs.wpovernight.com/category/woocommerce-pdf-invoices-packing-slips/" target="_blank"><?php _e( 'Invoices & Packing Slips' , 'woocommerce-pdf-invoices-packing-slips' ); ?></a></p>
|
5 |
+
<h2><?php _e( 'Happy selling!' , 'woocommerce-pdf-invoices-packing-slips' ); ?></h2>
|
6 |
</div>
|
includes/views/setup-wizard/logo.php
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
-
<div class="wpo-step-description">
|
2 |
-
<h2><?php _e( 'Your logo' , 'woocommerce-pdf-invoices-packing-slips' ); ?></h2>
|
3 |
-
<p><?php _e( 'Set the header image that will display on your invoice.' , 'woocommerce-pdf-invoices-packing-slips' ); ?></p>
|
4 |
-
</div>
|
5 |
-
<div class="wpo-setup-input">
|
6 |
-
<img src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" width="100%" height="20px" alt="" id="img-header_logo"/>
|
7 |
-
<input id="header_logo" name="wcpdf_settings[wpo_wcpdf_settings_general][header_logo]" type="hidden" value="" />
|
8 |
-
<span class="button wpo_upload_image_button header_logo" data-uploader_title="<?php _e( 'Select or upload your invoice header/logo', 'woocommerce-pdf-invoices-packing-slips' ); ?>" data-uploader_button_text="<?php _e( 'Set image', 'woocommerce-pdf-invoices-packing-slips' ); ?>" data-remove_button_text="<?php _e( 'Remove image', 'woocommerce-pdf-invoices-packing-slips' ); ?>" data-input_id="header_logo"><?php _e( 'Set image', 'woocommerce-pdf-invoices-packing-slips' ); ?></span>
|
9 |
</div>
|
1 |
+
<div class="wpo-step-description">
|
2 |
+
<h2><?php _e( 'Your logo' , 'woocommerce-pdf-invoices-packing-slips' ); ?></h2>
|
3 |
+
<p><?php _e( 'Set the header image that will display on your invoice.' , 'woocommerce-pdf-invoices-packing-slips' ); ?></p>
|
4 |
+
</div>
|
5 |
+
<div class="wpo-setup-input">
|
6 |
+
<img src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" width="100%" height="20px" alt="" id="img-header_logo"/>
|
7 |
+
<input id="header_logo" name="wcpdf_settings[wpo_wcpdf_settings_general][header_logo]" type="hidden" value="" />
|
8 |
+
<span class="button wpo_upload_image_button header_logo" data-uploader_title="<?php _e( 'Select or upload your invoice header/logo', 'woocommerce-pdf-invoices-packing-slips' ); ?>" data-uploader_button_text="<?php _e( 'Set image', 'woocommerce-pdf-invoices-packing-slips' ); ?>" data-remove_button_text="<?php _e( 'Remove image', 'woocommerce-pdf-invoices-packing-slips' ); ?>" data-input_id="header_logo"><?php _e( 'Set image', 'woocommerce-pdf-invoices-packing-slips' ); ?></span>
|
9 |
</div>
|
includes/views/setup-wizard/paper-format.php
CHANGED
@@ -1,14 +1,14 @@
|
|
1 |
-
<div class="wpo-step-description">
|
2 |
-
<h2><?php _e( 'Paper format', 'woocommerce-pdf-invoices-packing-slips' ); ?></h2>
|
3 |
-
<p><?php _e( 'Select the paper format for your invoice.', 'woocommerce-pdf-invoices-packing-slips' ); ?></p>
|
4 |
-
</div>
|
5 |
-
<div class="wpo-setup-input">
|
6 |
-
<?php
|
7 |
-
$current_settings = get_option( 'wpo_wcpdf_settings_general', array() );
|
8 |
-
?>
|
9 |
-
|
10 |
-
<select name="wcpdf_settings[wpo_wcpdf_settings_general][paper_size]">
|
11 |
-
<option <?php echo $current_settings['paper_size'] == 'a4' ? 'selected' : ''; ?> value="a4"><?php _e( 'A4', 'woocommerce-pdf-invoices-packing-slips' ); ?></option>
|
12 |
-
<option <?php echo $current_settings['paper_size'] == 'letter' ? 'selected' : ''; ?> value="letter"><?php _e( 'Letter', 'woocommerce-pdf-invoices-packing-slips' ); ?></option>
|
13 |
-
</select>
|
14 |
</div>
|
1 |
+
<div class="wpo-step-description">
|
2 |
+
<h2><?php _e( 'Paper format', 'woocommerce-pdf-invoices-packing-slips' ); ?></h2>
|
3 |
+
<p><?php _e( 'Select the paper format for your invoice.', 'woocommerce-pdf-invoices-packing-slips' ); ?></p>
|
4 |
+
</div>
|
5 |
+
<div class="wpo-setup-input">
|
6 |
+
<?php
|
7 |
+
$current_settings = get_option( 'wpo_wcpdf_settings_general', array() );
|
8 |
+
?>
|
9 |
+
|
10 |
+
<select name="wcpdf_settings[wpo_wcpdf_settings_general][paper_size]">
|
11 |
+
<option <?php echo $current_settings['paper_size'] == 'a4' ? 'selected' : ''; ?> value="a4"><?php _e( 'A4', 'woocommerce-pdf-invoices-packing-slips' ); ?></option>
|
12 |
+
<option <?php echo $current_settings['paper_size'] == 'letter' ? 'selected' : ''; ?> value="letter"><?php _e( 'Letter', 'woocommerce-pdf-invoices-packing-slips' ); ?></option>
|
13 |
+
</select>
|
14 |
</div>
|
includes/views/setup-wizard/shop-name.php
CHANGED
@@ -1,11 +1,11 @@
|
|
1 |
-
<div class="wpo-step-description">
|
2 |
-
<h2><?php _e( 'Enter your shop name', 'woocommerce-pdf-invoices-packing-slips' ); ?></h2>
|
3 |
-
<p><?php _e( 'Lets quickly setup your invoice. Please enter the name and address of your shop in the fields on the right.', 'woocommerce-pdf-invoices-packing-slips' ); ?></p>
|
4 |
-
</div>
|
5 |
-
<div class="wpo-setup-input">
|
6 |
-
<?php
|
7 |
-
$current_settings = get_option( 'wpo_wcpdf_settings_general', array() );
|
8 |
-
?>
|
9 |
-
<input type="text" class="shop-name" placeholder="Shop name" name="wcpdf_settings[wpo_wcpdf_settings_general][shop_name][default]" value="<?php echo !empty($current_settings['shop_name']) ? array_pop($current_settings['shop_name']) : get_bloginfo( 'name' ); ?>">
|
10 |
-
<textarea class="shop-address" placeholder="Shop address" name="wcpdf_settings[wpo_wcpdf_settings_general][shop_address][default]"><?php echo isset($current_settings['shop_address']) ? array_pop($current_settings['shop_address']) : ''; ?></textarea>
|
11 |
</div>
|
1 |
+
<div class="wpo-step-description">
|
2 |
+
<h2><?php _e( 'Enter your shop name', 'woocommerce-pdf-invoices-packing-slips' ); ?></h2>
|
3 |
+
<p><?php _e( 'Lets quickly setup your invoice. Please enter the name and address of your shop in the fields on the right.', 'woocommerce-pdf-invoices-packing-slips' ); ?></p>
|
4 |
+
</div>
|
5 |
+
<div class="wpo-setup-input">
|
6 |
+
<?php
|
7 |
+
$current_settings = get_option( 'wpo_wcpdf_settings_general', array() );
|
8 |
+
?>
|
9 |
+
<input type="text" class="shop-name" placeholder="Shop name" name="wcpdf_settings[wpo_wcpdf_settings_general][shop_name][default]" value="<?php echo !empty($current_settings['shop_name']) ? array_pop($current_settings['shop_name']) : get_bloginfo( 'name' ); ?>">
|
10 |
+
<textarea class="shop-address" placeholder="Shop address" name="wcpdf_settings[wpo_wcpdf_settings_general][shop_address][default]"><?php echo isset($current_settings['shop_address']) ? array_pop($current_settings['shop_address']) : ''; ?></textarea>
|
11 |
</div>
|
includes/views/wcpdf-extensions.php
CHANGED
@@ -1,131 +1,131 @@
|
|
1 |
-
<script type="text/javascript">
|
2 |
-
jQuery(document).ready(function() {
|
3 |
-
jQuery('.extensions .more').hide();
|
4 |
-
|
5 |
-
jQuery('.extensions > li').click(function() {
|
6 |
-
jQuery(this).toggleClass('expanded');
|
7 |
-
jQuery(this).find('.more').slideToggle();
|
8 |
-
});
|
9 |
-
});
|
10 |
-
</script>
|
11 |
-
|
12 |
-
<div class="wcpdf-extensions-ad">
|
13 |
-
<?php $no_pro = !class_exists('WooCommerce_PDF_IPS_Pro') && !class_exists('WooCommerce_PDF_IPS_Dropbox') && !class_exists('WooCommerce_PDF_IPS_Templates'); ?>
|
14 |
-
<img src="<?php echo WPO_WCPDF()->plugin_url() . '/assets/images/wpo-helper.png'; ?>" class="wpo-helper">
|
15 |
-
<h3><?php _e( 'Check out these premium extensions!', 'woocommerce-pdf-invoices-packing-slips' ); ?></h3>
|
16 |
-
<i>(<?php _e( 'click items to read more', 'woocommerce-pdf-invoices-packing-slips' ); ?>)</i>
|
17 |
-
<ul class="extensions">
|
18 |
-
<?php if ( $no_pro ): ?>
|
19 |
-
<!-- No Pro extensions: Ad for PDF bundle -->
|
20 |
-
<li>
|
21 |
-
<?php _e('Premium PDF Invoice bundle: Everything you need for a perfect invoicing system', 'woocommerce-pdf-invoices-packing-slips' )?>
|
22 |
-
<div class="more" style="display:none;">
|
23 |
-
<h4><?php _e( 'Supercharge WooCommerce PDF Invoices & Packing Slips with the all our premium extensions:', 'woocommerce-pdf-invoices-packing-slips' ); ?></h4>
|
24 |
-
<?php _e( 'Professional features:', 'woocommerce-pdf-invoices-packing-slips' ); ?>
|
25 |
-
<ul>
|
26 |
-
<li><?php _e( 'Email/print/download <b>PDF Credit Notes & Proforma invoices</b>', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
27 |
-
<li><?php _e( 'Send out a separate <b>notification email</b> with (or without) PDF invoices/packing slips, for example to a drop-shipper or a supplier.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
28 |
-
<li><?php _e( 'Attach <b>up to 3 static files</b> (for example a terms & conditions document) to the WooCommerce emails of your choice.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
29 |
-
<li><?php _e( 'Use <b>separate numbering systems</b> and/or format for proforma invoices and credit notes or utilize the main invoice numbering system', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
30 |
-
<li><?php _e( '<b>Customize</b> the <b>shipping & billing address</b> format to include additional custom fields, font sizes etc. without the need to create a custom template.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
31 |
-
<li><?php _e( 'Use the plugin in multilingual <b>WPML</b> setups', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
32 |
-
</ul>
|
33 |
-
<?php _e('Advanced, customizable templates', 'woocommerce-pdf-invoices-packing-slips' )?>
|
34 |
-
<ul>
|
35 |
-
<li><?php _e( 'Completely customize the invoice contents (prices, taxes, thumbnails) to your needs with a drag & drop customizer', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
36 |
-
<li><?php _e( 'Two extra stylish premade templates (Modern & Business)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
37 |
-
</ul>
|
38 |
-
<?php _e('Upload automatically to dropbox', 'woocommerce-pdf-invoices-packing-slips' )?>
|
39 |
-
<ul>
|
40 |
-
<li><?php _e( 'This extension conveniently uploads all the invoices (and other pdf documents from the professional extension) that are emailed to your customers to Dropbox. The best way to keep your invoice administration up to date!', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
41 |
-
</ul>
|
42 |
-
<br>
|
43 |
-
<a href="https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-bundle/" target="_blank"><?php _e("Get WooCommerce PDF Invoices & Packing Slips Bundle", 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
44 |
-
</div>
|
45 |
-
</li>
|
46 |
-
<?php endif; ?>
|
47 |
-
<?php
|
48 |
-
// NO BUNDLE: separate ads
|
49 |
-
if (!class_exists('WooCommerce_PDF_IPS_Pro') && !$no_pro) {
|
50 |
-
?>
|
51 |
-
<li>
|
52 |
-
<?php _e('Go Pro: Proforma invoices, credit notes (=refunds) & more!', 'woocommerce-pdf-invoices-packing-slips' )?>
|
53 |
-
<div class="more" style="display:none;">
|
54 |
-
<?php _e( 'Supercharge WooCommerce PDF Invoices & Packing Slips with the following features:', 'woocommerce-pdf-invoices-packing-slips' ); ?>
|
55 |
-
<ul>
|
56 |
-
<li><?php _e( 'Email/print/download <b>PDF Credit Notes & Proforma invoices</b>', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
57 |
-
<li><?php _e( 'Send out a separate <b>notification email</b> with (or without) PDF invoices/packing slips, for example to a drop-shipper or a supplier.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
58 |
-
<li><?php _e( 'Attach <b>up to 3 static files</b> (for example a terms & conditions document) to the WooCommerce emails of your choice.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
59 |
-
<li><?php _e( 'Use <b>separate numbering systems</b> and/or format for proforma invoices and credit notes or utilize the main invoice numbering system', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
60 |
-
<li><?php _e( '<b>Customize</b> the <b>shipping & billing address</b> format to include additional custom fields, font sizes etc. without the need to create a custom template.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
61 |
-
<li><?php _e( 'Use the plugin in multilingual <b>WPML</b> setups', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
62 |
-
<li><?php _e( 'Upload automatically to dropbox', 'woocommerce-pdf-invoices-packing-slips' )?></li>
|
63 |
-
</ul>
|
64 |
-
<a href="https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-professional/" target="_blank"><?php _e("Get WooCommerce PDF Invoices & Packing Slips Professional!", 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
65 |
-
</li>
|
66 |
-
<?php } ?>
|
67 |
-
|
68 |
-
<?php
|
69 |
-
if (!class_exists('WPO_WC_Smart_Reminder_Emails')) {
|
70 |
-
?>
|
71 |
-
<li>
|
72 |
-
<?php _e('Automatically send payment reminders to your customers', 'woocommerce-pdf-invoices-packing-slips' )?>
|
73 |
-
<div class="more" style="display:none;">
|
74 |
-
<?php _e('WooCommerce Smart Reminder emails', 'woocommerce-pdf-invoices-packing-slips' )?>
|
75 |
-
<ul>
|
76 |
-
<li><?php _e( '<b>Completely automatic</b> scheduled emails', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
77 |
-
<li><?php _e( '<b>Rich text editor</b> for the email text, including placeholders for data from the order (name, order total, etc)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
78 |
-
<li><?php _e( 'Configure the exact requirements for sending an email (time after order, order status, payment method)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
79 |
-
<li><?php _e( 'Fully <b>WPML Compatible</b> – emails will be automatically sent in the order language.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
80 |
-
<li><?php _e( '<b>Super versatile!</b> Can be used for any kind of reminder email (review reminders, repeat purchases)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
81 |
-
<li><b><?php _e( 'Integrates seamlessly with the PDF Invoices & Packing Slips plugin', 'woocommerce-pdf-invoices-packing-slips' ); ?></b></li>
|
82 |
-
</ul>
|
83 |
-
<a href="https://wpovernight.com/downloads/woocommerce-reminder-emails-payment-reminders/" target="_blank"><?php _e("Get WooCommerce Smart Reminder Emails", 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
84 |
-
</div>
|
85 |
-
</li>
|
86 |
-
<?php } ?>
|
87 |
-
|
88 |
-
<?php
|
89 |
-
if (!class_exists('WooCommerce_Ext_PrintOrders')) {
|
90 |
-
?>
|
91 |
-
<li>
|
92 |
-
<?php _e('Automatically send new orders or packing slips to your printer, as soon as the customer orders!', 'woocommerce-pdf-invoices-packing-slips' )?>
|
93 |
-
<div class="more" style="display:none;">
|
94 |
-
<table>
|
95 |
-
<tr>
|
96 |
-
<td><img src="<?php echo WPO_WCPDF()->plugin_url() . '/assets/images/cloud-print.png'; ?>" class="cloud-logo"></td>
|
97 |
-
<td>
|
98 |
-
<?php _e( 'Check out the WooCommerce Automatic Order Printing extension from our partners at Simba Hosting', 'woocommerce-pdf-invoices-packing-slips' ); ?><br/>
|
99 |
-
<a href="https://www.simbahosting.co.uk/s3/product/woocommerce-automatic-order-printing/?affiliates=2" target="_blank"><?php _e("WooCommerce Automatic Order Printing", 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
100 |
-
</td>
|
101 |
-
</tr>
|
102 |
-
</table>
|
103 |
-
</div>
|
104 |
-
</li>
|
105 |
-
<?php } ?>
|
106 |
-
|
107 |
-
<?php
|
108 |
-
if (!class_exists('WooCommerce_PDF_IPS_Templates') && !class_exists('WPO_WCPDF_Templates') && !$no_pro) {
|
109 |
-
$template_link = '<a href="https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/" target="_blank">wpovernight.com</a>';
|
110 |
-
$email_link = '<a href="mailto:support@wpovernight.com">support@wpovernight.com</a>'
|
111 |
-
?>
|
112 |
-
<li>
|
113 |
-
<?php _e('Advanced, customizable templates', 'woocommerce-pdf-invoices-packing-slips' )?>
|
114 |
-
<div class="more" style="display:none;">
|
115 |
-
<ul>
|
116 |
-
<li><?php _e( 'Completely customize the invoice contents (prices, taxes, thumbnails) to your needs with a drag & drop customizer', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
117 |
-
<li><?php _e( 'Two extra stylish premade templates (Modern & Business)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
118 |
-
<li><?php printf( __("Check out the Premium PDF Invoice & Packing Slips templates at %s.", 'woocommerce-pdf-invoices-packing-slips' ), $template_link );?></li>
|
119 |
-
<li><?php printf( __("For custom templates, contact us at %s.", 'woocommerce-pdf-invoices-packing-slips' ), $email_link );?></li>
|
120 |
-
</ul>
|
121 |
-
</div>
|
122 |
-
</li>
|
123 |
-
<?php } ?>
|
124 |
-
</ul>
|
125 |
-
<?php
|
126 |
-
// link to hide message when one of the premium extensions is installed
|
127 |
-
if ( class_exists('WooCommerce_PDF_IPS_Pro') || class_exists('WooCommerce_PDF_IPS_Dropbox') || class_exists('WooCommerce_PDF_IPS_Templates') || class_exists('WooCommerce_Ext_PrintOrders') || class_exists('WPO_WC_Smart_Reminder_Emails') ) {
|
128 |
-
printf('<a href="%s" style="display:inline-block; margin-top: 10px;">%s</a>', add_query_arg( 'wpo_wcpdf_hide_extensions_ad', 'true' ), __( 'Hide this message', 'woocommerce-pdf-invoices-packing-slips' ) );
|
129 |
-
}
|
130 |
-
?>
|
131 |
</div>
|
1 |
+
<script type="text/javascript">
|
2 |
+
jQuery(document).ready(function() {
|
3 |
+
jQuery('.extensions .more').hide();
|
4 |
+
|
5 |
+
jQuery('.extensions > li').click(function() {
|
6 |
+
jQuery(this).toggleClass('expanded');
|
7 |
+
jQuery(this).find('.more').slideToggle();
|
8 |
+
});
|
9 |
+
});
|
10 |
+
</script>
|
11 |
+
|
12 |
+
<div class="wcpdf-extensions-ad">
|
13 |
+
<?php $no_pro = !class_exists('WooCommerce_PDF_IPS_Pro') && !class_exists('WooCommerce_PDF_IPS_Dropbox') && !class_exists('WooCommerce_PDF_IPS_Templates'); ?>
|
14 |
+
<img src="<?php echo WPO_WCPDF()->plugin_url() . '/assets/images/wpo-helper.png'; ?>" class="wpo-helper">
|
15 |
+
<h3><?php _e( 'Check out these premium extensions!', 'woocommerce-pdf-invoices-packing-slips' ); ?></h3>
|
16 |
+
<i>(<?php _e( 'click items to read more', 'woocommerce-pdf-invoices-packing-slips' ); ?>)</i>
|
17 |
+
<ul class="extensions">
|
18 |
+
<?php if ( $no_pro ): ?>
|
19 |
+
<!-- No Pro extensions: Ad for PDF bundle -->
|
20 |
+
<li>
|
21 |
+
<?php _e('Premium PDF Invoice bundle: Everything you need for a perfect invoicing system', 'woocommerce-pdf-invoices-packing-slips' )?>
|
22 |
+
<div class="more" style="display:none;">
|
23 |
+
<h4><?php _e( 'Supercharge WooCommerce PDF Invoices & Packing Slips with the all our premium extensions:', 'woocommerce-pdf-invoices-packing-slips' ); ?></h4>
|
24 |
+
<?php _e( 'Professional features:', 'woocommerce-pdf-invoices-packing-slips' ); ?>
|
25 |
+
<ul>
|
26 |
+
<li><?php _e( 'Email/print/download <b>PDF Credit Notes & Proforma invoices</b>', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
27 |
+
<li><?php _e( 'Send out a separate <b>notification email</b> with (or without) PDF invoices/packing slips, for example to a drop-shipper or a supplier.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
28 |
+
<li><?php _e( 'Attach <b>up to 3 static files</b> (for example a terms & conditions document) to the WooCommerce emails of your choice.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
29 |
+
<li><?php _e( 'Use <b>separate numbering systems</b> and/or format for proforma invoices and credit notes or utilize the main invoice numbering system', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
30 |
+
<li><?php _e( '<b>Customize</b> the <b>shipping & billing address</b> format to include additional custom fields, font sizes etc. without the need to create a custom template.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
31 |
+
<li><?php _e( 'Use the plugin in multilingual <b>WPML</b> setups', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
32 |
+
</ul>
|
33 |
+
<?php _e('Advanced, customizable templates', 'woocommerce-pdf-invoices-packing-slips' )?>
|
34 |
+
<ul>
|
35 |
+
<li><?php _e( 'Completely customize the invoice contents (prices, taxes, thumbnails) to your needs with a drag & drop customizer', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
36 |
+
<li><?php _e( 'Two extra stylish premade templates (Modern & Business)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
37 |
+
</ul>
|
38 |
+
<?php _e('Upload automatically to dropbox', 'woocommerce-pdf-invoices-packing-slips' )?>
|
39 |
+
<ul>
|
40 |
+
<li><?php _e( 'This extension conveniently uploads all the invoices (and other pdf documents from the professional extension) that are emailed to your customers to Dropbox. The best way to keep your invoice administration up to date!', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
41 |
+
</ul>
|
42 |
+
<br>
|
43 |
+
<a href="https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-bundle/" target="_blank"><?php _e("Get WooCommerce PDF Invoices & Packing Slips Bundle", 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
44 |
+
</div>
|
45 |
+
</li>
|
46 |
+
<?php endif; ?>
|
47 |
+
<?php
|
48 |
+
// NO BUNDLE: separate ads
|
49 |
+
if (!class_exists('WooCommerce_PDF_IPS_Pro') && !$no_pro) {
|
50 |
+
?>
|
51 |
+
<li>
|
52 |
+
<?php _e('Go Pro: Proforma invoices, credit notes (=refunds) & more!', 'woocommerce-pdf-invoices-packing-slips' )?>
|
53 |
+
<div class="more" style="display:none;">
|
54 |
+
<?php _e( 'Supercharge WooCommerce PDF Invoices & Packing Slips with the following features:', 'woocommerce-pdf-invoices-packing-slips' ); ?>
|
55 |
+
<ul>
|
56 |
+
<li><?php _e( 'Email/print/download <b>PDF Credit Notes & Proforma invoices</b>', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
57 |
+
<li><?php _e( 'Send out a separate <b>notification email</b> with (or without) PDF invoices/packing slips, for example to a drop-shipper or a supplier.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
58 |
+
<li><?php _e( 'Attach <b>up to 3 static files</b> (for example a terms & conditions document) to the WooCommerce emails of your choice.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
59 |
+
<li><?php _e( 'Use <b>separate numbering systems</b> and/or format for proforma invoices and credit notes or utilize the main invoice numbering system', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
60 |
+
<li><?php _e( '<b>Customize</b> the <b>shipping & billing address</b> format to include additional custom fields, font sizes etc. without the need to create a custom template.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
61 |
+
<li><?php _e( 'Use the plugin in multilingual <b>WPML</b> setups', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
62 |
+
<li><?php _e( 'Upload automatically to dropbox', 'woocommerce-pdf-invoices-packing-slips' )?></li>
|
63 |
+
</ul>
|
64 |
+
<a href="https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-professional/" target="_blank"><?php _e("Get WooCommerce PDF Invoices & Packing Slips Professional!", 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
65 |
+
</li>
|
66 |
+
<?php } ?>
|
67 |
+
|
68 |
+
<?php
|
69 |
+
if (!class_exists('WPO_WC_Smart_Reminder_Emails')) {
|
70 |
+
?>
|
71 |
+
<li>
|
72 |
+
<?php _e('Automatically send payment reminders to your customers', 'woocommerce-pdf-invoices-packing-slips' )?>
|
73 |
+
<div class="more" style="display:none;">
|
74 |
+
<?php _e('WooCommerce Smart Reminder emails', 'woocommerce-pdf-invoices-packing-slips' )?>
|
75 |
+
<ul>
|
76 |
+
<li><?php _e( '<b>Completely automatic</b> scheduled emails', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
77 |
+
<li><?php _e( '<b>Rich text editor</b> for the email text, including placeholders for data from the order (name, order total, etc)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
78 |
+
<li><?php _e( 'Configure the exact requirements for sending an email (time after order, order status, payment method)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
79 |
+
<li><?php _e( 'Fully <b>WPML Compatible</b> – emails will be automatically sent in the order language.', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
80 |
+
<li><?php _e( '<b>Super versatile!</b> Can be used for any kind of reminder email (review reminders, repeat purchases)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
81 |
+
<li><b><?php _e( 'Integrates seamlessly with the PDF Invoices & Packing Slips plugin', 'woocommerce-pdf-invoices-packing-slips' ); ?></b></li>
|
82 |
+
</ul>
|
83 |
+
<a href="https://wpovernight.com/downloads/woocommerce-reminder-emails-payment-reminders/" target="_blank"><?php _e("Get WooCommerce Smart Reminder Emails", 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
84 |
+
</div>
|
85 |
+
</li>
|
86 |
+
<?php } ?>
|
87 |
+
|
88 |
+
<?php
|
89 |
+
if (!class_exists('WooCommerce_Ext_PrintOrders')) {
|
90 |
+
?>
|
91 |
+
<li>
|
92 |
+
<?php _e('Automatically send new orders or packing slips to your printer, as soon as the customer orders!', 'woocommerce-pdf-invoices-packing-slips' )?>
|
93 |
+
<div class="more" style="display:none;">
|
94 |
+
<table>
|
95 |
+
<tr>
|
96 |
+
<td><img src="<?php echo WPO_WCPDF()->plugin_url() . '/assets/images/cloud-print.png'; ?>" class="cloud-logo"></td>
|
97 |
+
<td>
|
98 |
+
<?php _e( 'Check out the WooCommerce Automatic Order Printing extension from our partners at Simba Hosting', 'woocommerce-pdf-invoices-packing-slips' ); ?><br/>
|
99 |
+
<a href="https://www.simbahosting.co.uk/s3/product/woocommerce-automatic-order-printing/?affiliates=2" target="_blank"><?php _e("WooCommerce Automatic Order Printing", 'woocommerce-pdf-invoices-packing-slips' ); ?></a>
|
100 |
+
</td>
|
101 |
+
</tr>
|
102 |
+
</table>
|
103 |
+
</div>
|
104 |
+
</li>
|
105 |
+
<?php } ?>
|
106 |
+
|
107 |
+
<?php
|
108 |
+
if (!class_exists('WooCommerce_PDF_IPS_Templates') && !class_exists('WPO_WCPDF_Templates') && !$no_pro) {
|
109 |
+
$template_link = '<a href="https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/" target="_blank">wpovernight.com</a>';
|
110 |
+
$email_link = '<a href="mailto:support@wpovernight.com">support@wpovernight.com</a>'
|
111 |
+
?>
|
112 |
+
<li>
|
113 |
+
<?php _e('Advanced, customizable templates', 'woocommerce-pdf-invoices-packing-slips' )?>
|
114 |
+
<div class="more" style="display:none;">
|
115 |
+
<ul>
|
116 |
+
<li><?php _e( 'Completely customize the invoice contents (prices, taxes, thumbnails) to your needs with a drag & drop customizer', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
117 |
+
<li><?php _e( 'Two extra stylish premade templates (Modern & Business)', 'woocommerce-pdf-invoices-packing-slips' ); ?></li>
|
118 |
+
<li><?php printf( __("Check out the Premium PDF Invoice & Packing Slips templates at %s.", 'woocommerce-pdf-invoices-packing-slips' ), $template_link );?></li>
|
119 |
+
<li><?php printf( __("For custom templates, contact us at %s.", 'woocommerce-pdf-invoices-packing-slips' ), $email_link );?></li>
|
120 |
+
</ul>
|
121 |
+
</div>
|
122 |
+
</li>
|
123 |
+
<?php } ?>
|
124 |
+
</ul>
|
125 |
+
<?php
|
126 |
+
// link to hide message when one of the premium extensions is installed
|
127 |
+
if ( class_exists('WooCommerce_PDF_IPS_Pro') || class_exists('WooCommerce_PDF_IPS_Dropbox') || class_exists('WPO_WCPDF_Templates') || class_exists('WooCommerce_PDF_IPS_Templates') || class_exists('WooCommerce_Ext_PrintOrders') || class_exists('WPO_WC_Smart_Reminder_Emails') ) {
|
128 |
+
printf('<a href="%s" style="display:inline-block; margin-top: 10px;">%s</a>', add_query_arg( 'wpo_wcpdf_hide_extensions_ad', 'true' ), __( 'Hide this message', 'woocommerce-pdf-invoices-packing-slips' ) );
|
129 |
+
}
|
130 |
+
?>
|
131 |
</div>
|
includes/views/wcpdf-settings-page.php
CHANGED
@@ -1,51 +1,51 @@
|
|
1 |
-
<script type="text/javascript">
|
2 |
-
jQuery( function( $ ) {
|
3 |
-
$("#footer-thankyou").html("If you like <strong>WooCommerce PDF Invoices & Packing Slips</strong> please leave us a <a href='https://wordpress.org/support/view/plugin-reviews/woocommerce-pdf-invoices-packing-slips?rate=5#postform'>★★★★★</a> rating. A huge thank you in advance!");
|
4 |
-
});
|
5 |
-
</script>
|
6 |
-
<div class="wrap">
|
7 |
-
<div class="icon32" id="icon-options-general"><br /></div>
|
8 |
-
<h2><?php _e( 'WooCommerce PDF Invoices', 'woocommerce-pdf-invoices-packing-slips' ); ?></h2>
|
9 |
-
<h2 class="nav-tab-wrapper">
|
10 |
-
<?php
|
11 |
-
foreach ($settings_tabs as $tab_slug => $tab_title ) {
|
12 |
-
$tab_link = esc_url("?page=wpo_wcpdf_options_page&tab={$tab_slug}");
|
13 |
-
printf('<a href="%1$s" class="nav-tab nav-tab-%2$s %3$s">%4$s</a>', $tab_link, $tab_slug, (($active_tab == $tab_slug) ? 'nav-tab-active' : ''), $tab_title);
|
14 |
-
}
|
15 |
-
?>
|
16 |
-
</h2>
|
17 |
-
|
18 |
-
<?php
|
19 |
-
do_action( 'wpo_wcpdf_before_settings_page', $active_tab, $active_section );
|
20 |
-
|
21 |
-
// save or check option to hide extensions ad
|
22 |
-
if ( isset( $_GET['wpo_wcpdf_hide_extensions_ad'] ) ) {
|
23 |
-
update_option( 'wpo_wcpdf_hide_extensions_ad', true );
|
24 |
-
$hide_ad = true;
|
25 |
-
} else {
|
26 |
-
$hide_ad = get_option( 'wpo_wcpdf_hide_extensions_ad' );
|
27 |
-
}
|
28 |
-
|
29 |
-
if ( !$hide_ad && !( class_exists('WooCommerce_PDF_IPS_Pro') && class_exists('WooCommerce_PDF_IPS_Dropbox') && class_exists('WooCommerce_PDF_IPS_Templates') && class_exists('WooCommerce_Ext_PrintOrders') ) ) {
|
30 |
-
include('wcpdf-extensions.php');
|
31 |
-
}
|
32 |
-
|
33 |
-
?>
|
34 |
-
<form method="post" action="options.php" id="wpo-wcpdf-settings" class="<?php echo "{$active_tab} {$active_section}"; ?>">
|
35 |
-
<?php
|
36 |
-
do_action( 'wpo_wcpdf_before_settings', $active_tab, $active_section );
|
37 |
-
if ( has_action( 'wpo_wcpdf_settings_output_'.$active_tab ) ) {
|
38 |
-
do_action( 'wpo_wcpdf_settings_output_'.$active_tab, $active_section );
|
39 |
-
} else {
|
40 |
-
// legacy settings
|
41 |
-
settings_fields( "wpo_wcpdf_{$active_tab}_settings" );
|
42 |
-
do_settings_sections( "wpo_wcpdf_{$active_tab}_settings" );
|
43 |
-
|
44 |
-
submit_button();
|
45 |
-
}
|
46 |
-
do_action( 'wpo_wcpdf_after_settings', $active_tab, $active_section );
|
47 |
-
?>
|
48 |
-
|
49 |
-
</form>
|
50 |
-
<?php do_action( 'wpo_wcpdf_after_settings_page', $active_tab, $active_section ); ?>
|
51 |
-
</div>
|
1 |
+
<script type="text/javascript">
|
2 |
+
jQuery( function( $ ) {
|
3 |
+
$("#footer-thankyou").html("If you like <strong>WooCommerce PDF Invoices & Packing Slips</strong> please leave us a <a href='https://wordpress.org/support/view/plugin-reviews/woocommerce-pdf-invoices-packing-slips?rate=5#postform'>★★★★★</a> rating. A huge thank you in advance!");
|
4 |
+
});
|
5 |
+
</script>
|
6 |
+
<div class="wrap">
|
7 |
+
<div class="icon32" id="icon-options-general"><br /></div>
|
8 |
+
<h2><?php _e( 'WooCommerce PDF Invoices', 'woocommerce-pdf-invoices-packing-slips' ); ?></h2>
|
9 |
+
<h2 class="nav-tab-wrapper">
|
10 |
+
<?php
|
11 |
+
foreach ($settings_tabs as $tab_slug => $tab_title ) {
|
12 |
+
$tab_link = esc_url("?page=wpo_wcpdf_options_page&tab={$tab_slug}");
|
13 |
+
printf('<a href="%1$s" class="nav-tab nav-tab-%2$s %3$s">%4$s</a>', $tab_link, $tab_slug, (($active_tab == $tab_slug) ? 'nav-tab-active' : ''), $tab_title);
|
14 |
+
}
|
15 |
+
?>
|
16 |
+
</h2>
|
17 |
+
|
18 |
+
<?php
|
19 |
+
do_action( 'wpo_wcpdf_before_settings_page', $active_tab, $active_section );
|
20 |
+
|
21 |
+
// save or check option to hide extensions ad
|
22 |
+
if ( isset( $_GET['wpo_wcpdf_hide_extensions_ad'] ) ) {
|
23 |
+
update_option( 'wpo_wcpdf_hide_extensions_ad', true );
|
24 |
+
$hide_ad = true;
|
25 |
+
} else {
|
26 |
+
$hide_ad = get_option( 'wpo_wcpdf_hide_extensions_ad' );
|
27 |
+
}
|
28 |
+
|
29 |
+
if ( !$hide_ad && !( class_exists('WooCommerce_PDF_IPS_Pro') && class_exists('WooCommerce_PDF_IPS_Dropbox') && class_exists('WooCommerce_PDF_IPS_Templates') && class_exists('WooCommerce_Ext_PrintOrders') ) ) {
|
30 |
+
include('wcpdf-extensions.php');
|
31 |
+
}
|
32 |
+
|
33 |
+
?>
|
34 |
+
<form method="post" action="options.php" id="wpo-wcpdf-settings" class="<?php echo "{$active_tab} {$active_section}"; ?>">
|
35 |
+
<?php
|
36 |
+
do_action( 'wpo_wcpdf_before_settings', $active_tab, $active_section );
|
37 |
+
if ( has_action( 'wpo_wcpdf_settings_output_'.$active_tab ) ) {
|
38 |
+
do_action( 'wpo_wcpdf_settings_output_'.$active_tab, $active_section );
|
39 |
+
} else {
|
40 |
+
// legacy settings
|
41 |
+
settings_fields( "wpo_wcpdf_{$active_tab}_settings" );
|
42 |
+
do_settings_sections( "wpo_wcpdf_{$active_tab}_settings" );
|
43 |
+
|
44 |
+
submit_button();
|
45 |
+
}
|
46 |
+
do_action( 'wpo_wcpdf_after_settings', $active_tab, $active_section );
|
47 |
+
?>
|
48 |
+
|
49 |
+
</form>
|
50 |
+
<?php do_action( 'wpo_wcpdf_after_settings_page', $active_tab, $active_section ); ?>
|
51 |
+
</div>
|
readme.txt
CHANGED
@@ -1,221 +1,227 @@
|
|
1 |
-
=== WooCommerce PDF Invoices & Packing Slips ===
|
2 |
-
Contributors: pomegranate
|
3 |
-
Donate link: https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-bundle/
|
4 |
-
Tags: woocommerce, pdf, invoices, packing slips, print, delivery notes, invoice, packing slip, export, email, bulk, automatic
|
5 |
-
Requires at least: 3.5
|
6 |
-
Tested up to: 4.9
|
7 |
-
Requires PHP: 5.3
|
8 |
-
Stable tag: 2.1.
|
9 |
-
License: GPLv2 or later
|
10 |
-
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
11 |
-
|
12 |
-
Create, print & automatically email PDF invoices & packing slips for WooCommerce orders.
|
13 |
-
|
14 |
-
== Description ==
|
15 |
-
|
16 |
-
This WooCommerce extension automatically adds a PDF invoice to the order confirmation emails sent out to your customers. Includes a basic template (additional templates are available from [WP Overnight](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/)) as well as the possibility to modify/create your own templates. In addition, you can choose to download or print invoices and packing slips from the WooCommerce order admin.
|
17 |
-
|
18 |
-
= Main features =
|
19 |
-
* Automatically attach invoice PDF to WooCommerce emails of your choice
|
20 |
-
* Download the PDF invoice / packing slip from the order admin page
|
21 |
-
* Generate PDF invoices / packings slips in bulk
|
22 |
-
* **Fully customizable** HTML/CSS invoice templates
|
23 |
-
* Download invoices from the My Account page
|
24 |
-
* Sequential invoice numbers - with custom formatting
|
25 |
-
* **Available in: Czech, Dutch, English, Finnish, French, German, Hungarian, Italian, Japanese (see FAQ for adding custom fonts!), Norwegian, Polish, Romanian, Russian, Slovak, Slovenian, Spanish, Swedish & Ukrainian**
|
26 |
-
|
27 |
-
In addition to this, we offer several premium extensions:
|
28 |
-
|
29 |
-
* Create/email PDF Proforma Invoices, Credit Notes (for Refunds), email Packing Slips & more with [WooCommerce PDF Invoices & Packing Slips Professional](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-professional/)
|
30 |
-
* Upload all invoices automatically to Dropbox with [WooCommerce PDF Invoices & Packing Slips to Dropbox](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-dropbox/)
|
31 |
-
* Automatically send new orders or packing slips to your printer, as soon as the customer orders! [WooCommerce Automatic Order Printing](https://www.simbahosting.co.uk/s3/product/woocommerce-automatic-order-printing/?affiliates=2) (from our partners at Simba Hosting)
|
32 |
-
* More advanced & stylish templates with [WooCommerce PDF Invoices & Packing Slips Premium Templates](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/)
|
33 |
-
|
34 |
-
= Fully customizable =
|
35 |
-
In addition to a number of default settings (including a custom header/logo) and several layout fields that you can use out of the box, the plugin contains HTML/CSS based templates that allow for customization & full control over the PDF output. Copy the templates to your theme folder and you don't have to worry that your customizations will be overwritten when you update the plugin.
|
36 |
-
|
37 |
-
* Insert customer header image/logo
|
38 |
-
* Modify shop data / footer / disclaimer etc. on the invoices & packing slips
|
39 |
-
* Select paper size (Letter or A4)
|
40 |
-
* Translation ready
|
41 |
-
|
42 |
-
== Installation ==
|
43 |
-
|
44 |
-
= Minimum Requirements =
|
45 |
-
|
46 |
-
* WooCommerce 2.2 or later
|
47 |
-
* WordPress 3.5 or later
|
48 |
-
|
49 |
-
= Automatic installation =
|
50 |
-
Automatic installation is the easiest option as WordPress handles the file transfers itself and you don't even need to leave your web browser. To do an automatic install of WooCommerce PDF Invoices & Packing Slips, log in to your WordPress admin panel, navigate to the Plugins menu and click Add New.
|
51 |
-
|
52 |
-
In the search field type "WooCommerce PDF Invoices & Packing Slips" and click Search Plugins. You can install it by simply clicking Install Now. After clicking that link you will be asked if you're sure you want to install the plugin. Click yes and WordPress will automatically complete the installation. After installation has finished, click the 'activate plugin' link.
|
53 |
-
|
54 |
-
= Manual installation via the WordPress interface =
|
55 |
-
1. Download the plugin zip file to your computer
|
56 |
-
2. Go to the WordPress admin panel menu Plugins > Add New
|
57 |
-
3. Choose upload
|
58 |
-
4. Upload the plugin zip file, the plugin will now be installed
|
59 |
-
5. After installation has finished, click the 'activate plugin' link
|
60 |
-
|
61 |
-
= Manual installation via FTP =
|
62 |
-
1. Download the plugin file to your computer and unzip it
|
63 |
-
2. Using an FTP program, or your hosting control panel, upload the unzipped plugin folder to your WordPress installation's wp-content/plugins/ directory.
|
64 |
-
3. Activate the plugin from the Plugins menu within the WordPress admin.
|
65 |
-
|
66 |
-
== Frequently Asked Questions ==
|
67 |
-
|
68 |
-
= Where can I find the documentation? =
|
69 |
-
|
70 |
-
[WooCommerce PDF Invoices & Packing Slips documentation](http://docs.wpovernight.com/woocommerce-pdf-invoices-packing-slips/)
|
71 |
-
|
72 |
-
= It's not working! =
|
73 |
-
|
74 |
-
Check out our step by step diagnostic instructions here: https://wordpress.org/support/topic/read-this-first-9/
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
= Where can I find more templates? =
|
81 |
-
|
82 |
-
Go to [wpovernight.com](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/) to checkout more templates! These include templates with more tax details and product thumbnails. Need a custom templates? Contact us at support@wpovernight.com for more information.
|
83 |
-
|
84 |
-
= Can I create/send a proforma invoice or a credit note? =
|
85 |
-
This is a feature of our Professional extension, which can be found at [wpovernight.com](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-professional/)
|
86 |
-
|
87 |
-
= Can I contribute to the code? =
|
88 |
-
You're more than welcome! This plugin is hosted on github, where you can post issues or make pull requests.
|
89 |
-
https://github.com/wpovernight/woocommerce-pdf-invoices-packing-slips
|
90 |
-
|
91 |
-
= How can I display the HTML/CSS source for debugging/developing templates? =
|
92 |
-
There's a setting on the Status tab of the settings page that allows you to toggle HTML output. Don't forget to turn if off after you're done testing!
|
93 |
-
|
94 |
-
|
95 |
-
== Screenshots ==
|
96 |
-
|
97 |
-
1. Simple invoice PDF
|
98 |
-
2. Simple packing slip PDF
|
99 |
-
3. Quickly print individual invoices or packing slips from the order list
|
100 |
-
4. Print invoices or packing slips in bulk
|
101 |
-
5. Attach invoices to any WooCommerce email
|
102 |
-
6. Set shop name, address, header logo, etc.
|
103 |
-
|
104 |
-
== Changelog ==
|
105 |
-
|
106 |
-
= 2.1.
|
107 |
-
*
|
108 |
-
*
|
109 |
-
*
|
110 |
-
* Tweak:
|
111 |
-
|
112 |
-
= 2.1.
|
113 |
-
*
|
114 |
-
*
|
115 |
-
*
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
*
|
122 |
-
|
123 |
-
|
124 |
-
*
|
125 |
-
|
126 |
-
= 2.0.
|
127 |
-
*
|
128 |
-
*
|
129 |
-
*
|
130 |
-
* Dev: added `
|
131 |
-
|
132 |
-
= 2.0.
|
133 |
-
*
|
134 |
-
|
135 |
-
|
136 |
-
*
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
* Fix:
|
143 |
-
*
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
* Fix:
|
148 |
-
* Fix:
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
*
|
153 |
-
* Fix:
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
* Feature:
|
158 |
-
*
|
159 |
-
* Fix:
|
160 |
-
|
161 |
-
= 2.0.
|
162 |
-
* Feature:
|
163 |
-
*
|
164 |
-
* Fix: Invoice number
|
165 |
-
* Fix:
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
*
|
171 |
-
* Fix:
|
172 |
-
*
|
173 |
-
* Tweak:
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
* Fix:
|
178 |
-
*
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
* Fix:
|
184 |
-
* Fix:
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
* Fix:
|
190 |
-
* Fix:
|
191 |
-
*
|
192 |
-
*
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
* Fix:
|
198 |
-
|
199 |
-
|
200 |
-
*
|
201 |
-
|
202 |
-
|
203 |
-
*
|
204 |
-
|
205 |
-
|
206 |
-
* New:
|
207 |
-
*
|
208 |
-
*
|
209 |
-
*
|
210 |
-
*
|
211 |
-
*
|
212 |
-
|
213 |
-
|
214 |
-
*
|
215 |
-
* Fix:
|
216 |
-
*
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
221 |
2.0 is a BIG update! Make a full site backup before upgrading!
|
1 |
+
=== WooCommerce PDF Invoices & Packing Slips ===
|
2 |
+
Contributors: pomegranate
|
3 |
+
Donate link: https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-bundle/
|
4 |
+
Tags: woocommerce, pdf, invoices, packing slips, print, delivery notes, invoice, packing slip, export, email, bulk, automatic
|
5 |
+
Requires at least: 3.5
|
6 |
+
Tested up to: 4.9
|
7 |
+
Requires PHP: 5.3
|
8 |
+
Stable tag: 2.1.2
|
9 |
+
License: GPLv2 or later
|
10 |
+
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
11 |
+
|
12 |
+
Create, print & automatically email PDF invoices & packing slips for WooCommerce orders.
|
13 |
+
|
14 |
+
== Description ==
|
15 |
+
|
16 |
+
This WooCommerce extension automatically adds a PDF invoice to the order confirmation emails sent out to your customers. Includes a basic template (additional templates are available from [WP Overnight](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/)) as well as the possibility to modify/create your own templates. In addition, you can choose to download or print invoices and packing slips from the WooCommerce order admin.
|
17 |
+
|
18 |
+
= Main features =
|
19 |
+
* Automatically attach invoice PDF to WooCommerce emails of your choice
|
20 |
+
* Download the PDF invoice / packing slip from the order admin page
|
21 |
+
* Generate PDF invoices / packings slips in bulk
|
22 |
+
* **Fully customizable** HTML/CSS invoice templates
|
23 |
+
* Download invoices from the My Account page
|
24 |
+
* Sequential invoice numbers - with custom formatting
|
25 |
+
* **Available in: Czech, Dutch, English, Finnish, French, German, Hungarian, Italian, Japanese (see FAQ for adding custom fonts!), Norwegian, Polish, Romanian, Russian, Slovak, Slovenian, Spanish, Swedish & Ukrainian**
|
26 |
+
|
27 |
+
In addition to this, we offer several premium extensions:
|
28 |
+
|
29 |
+
* Create/email PDF Proforma Invoices, Credit Notes (for Refunds), email Packing Slips & more with [WooCommerce PDF Invoices & Packing Slips Professional](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-professional/)
|
30 |
+
* Upload all invoices automatically to Dropbox with [WooCommerce PDF Invoices & Packing Slips to Dropbox](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-dropbox/)
|
31 |
+
* Automatically send new orders or packing slips to your printer, as soon as the customer orders! [WooCommerce Automatic Order Printing](https://www.simbahosting.co.uk/s3/product/woocommerce-automatic-order-printing/?affiliates=2) (from our partners at Simba Hosting)
|
32 |
+
* More advanced & stylish templates with [WooCommerce PDF Invoices & Packing Slips Premium Templates](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/)
|
33 |
+
|
34 |
+
= Fully customizable =
|
35 |
+
In addition to a number of default settings (including a custom header/logo) and several layout fields that you can use out of the box, the plugin contains HTML/CSS based templates that allow for customization & full control over the PDF output. Copy the templates to your theme folder and you don't have to worry that your customizations will be overwritten when you update the plugin.
|
36 |
+
|
37 |
+
* Insert customer header image/logo
|
38 |
+
* Modify shop data / footer / disclaimer etc. on the invoices & packing slips
|
39 |
+
* Select paper size (Letter or A4)
|
40 |
+
* Translation ready
|
41 |
+
|
42 |
+
== Installation ==
|
43 |
+
|
44 |
+
= Minimum Requirements =
|
45 |
+
|
46 |
+
* WooCommerce 2.2 or later
|
47 |
+
* WordPress 3.5 or later
|
48 |
+
|
49 |
+
= Automatic installation =
|
50 |
+
Automatic installation is the easiest option as WordPress handles the file transfers itself and you don't even need to leave your web browser. To do an automatic install of WooCommerce PDF Invoices & Packing Slips, log in to your WordPress admin panel, navigate to the Plugins menu and click Add New.
|
51 |
+
|
52 |
+
In the search field type "WooCommerce PDF Invoices & Packing Slips" and click Search Plugins. You can install it by simply clicking Install Now. After clicking that link you will be asked if you're sure you want to install the plugin. Click yes and WordPress will automatically complete the installation. After installation has finished, click the 'activate plugin' link.
|
53 |
+
|
54 |
+
= Manual installation via the WordPress interface =
|
55 |
+
1. Download the plugin zip file to your computer
|
56 |
+
2. Go to the WordPress admin panel menu Plugins > Add New
|
57 |
+
3. Choose upload
|
58 |
+
4. Upload the plugin zip file, the plugin will now be installed
|
59 |
+
5. After installation has finished, click the 'activate plugin' link
|
60 |
+
|
61 |
+
= Manual installation via FTP =
|
62 |
+
1. Download the plugin file to your computer and unzip it
|
63 |
+
2. Using an FTP program, or your hosting control panel, upload the unzipped plugin folder to your WordPress installation's wp-content/plugins/ directory.
|
64 |
+
3. Activate the plugin from the Plugins menu within the WordPress admin.
|
65 |
+
|
66 |
+
== Frequently Asked Questions ==
|
67 |
+
|
68 |
+
= Where can I find the documentation? =
|
69 |
+
|
70 |
+
[WooCommerce PDF Invoices & Packing Slips documentation](http://docs.wpovernight.com/woocommerce-pdf-invoices-packing-slips/)
|
71 |
+
|
72 |
+
= It's not working! =
|
73 |
+
|
74 |
+
Check out our step by step diagnostic instructions here: https://wordpress.org/support/topic/read-this-first-9/
|
75 |
+
|
76 |
+
|
77 |
+
|
78 |
+
|
79 |
+
|
80 |
+
= Where can I find more templates? =
|
81 |
+
|
82 |
+
Go to [wpovernight.com](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/) to checkout more templates! These include templates with more tax details and product thumbnails. Need a custom templates? Contact us at support@wpovernight.com for more information.
|
83 |
+
|
84 |
+
= Can I create/send a proforma invoice or a credit note? =
|
85 |
+
This is a feature of our Professional extension, which can be found at [wpovernight.com](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-professional/)
|
86 |
+
|
87 |
+
= Can I contribute to the code? =
|
88 |
+
You're more than welcome! This plugin is hosted on github, where you can post issues or make pull requests.
|
89 |
+
https://github.com/wpovernight/woocommerce-pdf-invoices-packing-slips
|
90 |
+
|
91 |
+
= How can I display the HTML/CSS source for debugging/developing templates? =
|
92 |
+
There's a setting on the Status tab of the settings page that allows you to toggle HTML output. Don't forget to turn if off after you're done testing!
|
93 |
+
|
94 |
+
|
95 |
+
== Screenshots ==
|
96 |
+
|
97 |
+
1. Simple invoice PDF
|
98 |
+
2. Simple packing slip PDF
|
99 |
+
3. Quickly print individual invoices or packing slips from the order list
|
100 |
+
4. Print invoices or packing slips in bulk
|
101 |
+
5. Attach invoices to any WooCommerce email
|
102 |
+
6. Set shop name, address, header logo, etc.
|
103 |
+
|
104 |
+
== Changelog ==
|
105 |
+
|
106 |
+
= 2.1.2 =
|
107 |
+
* Feature: New action wpo_wcpdf_init_document
|
108 |
+
* Fix: Use title getters for my-account and backend buttons
|
109 |
+
* Fix: Legacy Premium Templates reference
|
110 |
+
* Tweak: Skip documents overview in settings, default to invoice
|
111 |
+
|
112 |
+
= 2.1.1 =
|
113 |
+
* Fix: WooCommerce Order Status & Actions Manager emails compatibility
|
114 |
+
* Feature: sort orders by invoice number column
|
115 |
+
* Tweak: pass document object to title filters
|
116 |
+
* Tweak: use title getter in template files (instead of title string)
|
117 |
+
|
118 |
+
= 2.1.0 =
|
119 |
+
* Feature: WooCommerce Order Status & Actions Manager emails compatibility
|
120 |
+
* Fix: Better url fallback for images stored in cloud
|
121 |
+
* Update: dompdf library updated to 0.8.2 - DOMDocument parser set to default again
|
122 |
+
|
123 |
+
= 2.0.15 =
|
124 |
+
* Fix: Prevent saving invoice number/date from order details page when not edited
|
125 |
+
|
126 |
+
= 2.0.14 =
|
127 |
+
* Feature: Manually resend specific order emails in WooCommerce 3.2+
|
128 |
+
* Tweak: Show full size logo preview in settings
|
129 |
+
* Tweak: Custom field fallback to underscore prefixed meta key
|
130 |
+
* Dev: added `wpo_wcpdf_before_sequential_number_increment` action
|
131 |
+
|
132 |
+
= 2.0.13 =
|
133 |
+
* Fix: Minor XSS issue on settings screens by escaping and sanitizing 'tab' & 'section' GET variables. Discovered by Detectify.
|
134 |
+
* Fix: Pakistani Rupee Symbol
|
135 |
+
* Feature: Automatically enable extended currency symbol support for currencies not supported by Open Sans
|
136 |
+
* Dev: added `wpo_wcpdf_document_number_settings` filter
|
137 |
+
|
138 |
+
= 2.0.12 =
|
139 |
+
* Option: Use different HTML parser (debug settings)
|
140 |
+
|
141 |
+
= 2.0.11 =
|
142 |
+
* Fix: Improved fonts update routine (now preserves custom fonts)
|
143 |
+
* Fix: Enable HTML5 parser by default (fixes issues with libxml)
|
144 |
+
* Tweak: Show both PHP & WP Memory limit in Status tab
|
145 |
+
|
146 |
+
= 2.0.10 =
|
147 |
+
* Fix: Set invoice number backend button
|
148 |
+
* Fix: Thumbail paths
|
149 |
+
* Tweak: Make dompdf options filterable
|
150 |
+
|
151 |
+
= 2.0.9 =
|
152 |
+
* Feature: use `[invoice_date="ymd"]` in invoice number prefix or suffix to include a specific date format in the invoice number
|
153 |
+
* Fix: Postmeta table prefix for invoice counter
|
154 |
+
* Fix: 0% tax rates
|
155 |
+
|
156 |
+
= 2.0.8 =
|
157 |
+
* Feature: Add support for Bedrock / alternative folder structures
|
158 |
+
* Dev: Filter for merged documents
|
159 |
+
* Fix: Better attributes fallback for product variations
|
160 |
+
|
161 |
+
= 2.0.7 =
|
162 |
+
* Feature: Added button to delete legacy settings
|
163 |
+
* Feature: Option to enable font subsetting
|
164 |
+
* Fix: Invoice number sequence for databases with alternative auto_increment_increment settings
|
165 |
+
* Fix: Fallback function for MB String (mb_stripos)
|
166 |
+
|
167 |
+
= 2.0.6 =
|
168 |
+
* Feature: Improved third party invoice number filters (`wpo_wcpdf_external_invoice_number_enabled` & `wpo_wcpdf_external_invoice_number`)
|
169 |
+
* Fix: Underline position for Open Sans font
|
170 |
+
* Fix: Invoice number auto_increment for servers that restarted frequently
|
171 |
+
* Fix: Dompdf log file location (preventing open base_dir notices breaking PDF header)
|
172 |
+
* Fix: 1.6.6 Settings migration duplicates merging
|
173 |
+
* Tweak: Clear fonts folder when manually reinstalling fonts
|
174 |
+
|
175 |
+
= 2.0.5 =
|
176 |
+
* Feature: Remove temporary files (Status tab)
|
177 |
+
* Fix: Page number replacement
|
178 |
+
* Tweak: Fallback functions for MB String extension
|
179 |
+
* Tweak: Improved wpo_wcpdf_check_privs usability for my account privileges
|
180 |
+
* Legacy support: added wc_price alias for format_price method in document
|
181 |
+
|
182 |
+
= 2.0.4 =
|
183 |
+
* Fix: Apply filters for custom invoice number formatting in document too
|
184 |
+
* Fix: Parent fallback for missing dates from refunds
|
185 |
+
|
186 |
+
= 2.0.3 =
|
187 |
+
* Fix: Better support for legacy invoice number filter (`wpo_wcpdf_invoice_number` - replaced by `wpo_wcpdf_formatted_document_number`)
|
188 |
+
* Fix: Document number formatting fallback to order date if no document date available
|
189 |
+
* Fix: Updated classmap: PSR loading didn't work on some installations
|
190 |
+
* Fix: Prevent order notes from all orders showing when document is not loaded properly in filter
|
191 |
+
* Tweak: Disable deprecation notices during email sending
|
192 |
+
* Tweak: ignore outdated language packs
|
193 |
+
|
194 |
+
= 2.0.2 =
|
195 |
+
* Fix: order notes using correct order_id
|
196 |
+
* Fix: WC3.0 deprecation notice for currency
|
197 |
+
* Fix: Avoid crashing on PHP5.2 and older
|
198 |
+
* Fix: Only use PHP MB String when present
|
199 |
+
* Fix: Remote images
|
200 |
+
* Fix: Download option
|
201 |
+
|
202 |
+
= 2.0.1 =
|
203 |
+
* Fix: PHP 5.4 issue
|
204 |
+
|
205 |
+
= 2.0.0 =
|
206 |
+
* New: Better structured & more advanced settings for documents
|
207 |
+
* New: Option to enable & disable Packing Slips or Invoices
|
208 |
+
* New: Invoice number sequence stored separately for improved speed & performance
|
209 |
+
* New: Completely rewritten codebase for more flexibility & better reliability
|
210 |
+
* New: Updated PDF library to DOMPDF 0.8
|
211 |
+
* New: PDF Library made pluggable (by using the `wpo_wcpdf_pdf_maker` filter)
|
212 |
+
* New: lots of new functions & filters to allow developers to hook into the plugin
|
213 |
+
* Changed: **$wpo_wcpdf variable is now deprecated** (legacy mode available & automatically enabled on update)
|
214 |
+
* Fix: Improved PHP 7 & 7.1 support
|
215 |
+
* Fix: Positive prices for refunds
|
216 |
+
* Fix: Use parent for attributes retrieved for product variations
|
217 |
+
* Fix: Set content type to PDF for download
|
218 |
+
|
219 |
+
= 1.6.6 =
|
220 |
+
* Feature: Facilitate downgrading from 2.0 (re-installing fonts & resetting version)
|
221 |
+
* Fix: Update currencies font (added Georgian Lari)
|
222 |
+
* Translations: Added Indonesian
|
223 |
+
|
224 |
+
== Upgrade Notice ==
|
225 |
+
|
226 |
+
= 2.1.1 =
|
227 |
2.0 is a BIG update! Make a full site backup before upgrading!
|
templates/Simple/invoice.php
CHANGED
@@ -1,144 +1,144 @@
|
|
1 |
-
<?php do_action( 'wpo_wcpdf_before_document', $this->type, $this->order ); ?>
|
2 |
-
|
3 |
-
<table class="head container">
|
4 |
-
<tr>
|
5 |
-
<td class="header">
|
6 |
-
<?php
|
7 |
-
if( $this->has_header_logo() ) {
|
8 |
-
$this->header_logo();
|
9 |
-
} else {
|
10 |
-
echo $this->get_title();
|
11 |
-
}
|
12 |
-
?>
|
13 |
-
</td>
|
14 |
-
<td class="shop-info">
|
15 |
-
<div class="shop-name"><h3><?php $this->shop_name(); ?></h3></div>
|
16 |
-
<div class="shop-address"><?php $this->shop_address(); ?></div>
|
17 |
-
</td>
|
18 |
-
</tr>
|
19 |
-
</table>
|
20 |
-
|
21 |
-
<h1 class="document-type-label">
|
22 |
-
<?php if( $this->has_header_logo() ) echo $this->get_title(); ?>
|
23 |
-
</h1>
|
24 |
-
|
25 |
-
<?php do_action( 'wpo_wcpdf_after_document_label', $this->type, $this->order ); ?>
|
26 |
-
|
27 |
-
<table class="order-data-addresses">
|
28 |
-
<tr>
|
29 |
-
<td class="address billing-address">
|
30 |
-
<!-- <h3><?php _e( 'Billing Address:', 'woocommerce-pdf-invoices-packing-slips' ); ?></h3> -->
|
31 |
-
<?php $this->billing_address(); ?>
|
32 |
-
<?php if ( isset($this->settings['display_email']) ) { ?>
|
33 |
-
<div class="billing-email"><?php $this->billing_email(); ?></div>
|
34 |
-
<?php } ?>
|
35 |
-
<?php if ( isset($this->settings['display_phone']) ) { ?>
|
36 |
-
<div class="billing-phone"><?php $this->billing_phone(); ?></div>
|
37 |
-
<?php } ?>
|
38 |
-
</td>
|
39 |
-
<td class="address shipping-address">
|
40 |
-
<?php if ( isset($this->settings['display_shipping_address']) && $this->ships_to_different_address()) { ?>
|
41 |
-
<h3><?php _e( 'Ship To:', 'woocommerce-pdf-invoices-packing-slips' ); ?></h3>
|
42 |
-
<?php $this->shipping_address(); ?>
|
43 |
-
<?php } ?>
|
44 |
-
</td>
|
45 |
-
<td class="order-data">
|
46 |
-
<table>
|
47 |
-
<?php do_action( 'wpo_wcpdf_before_order_data', $this->type, $this->order ); ?>
|
48 |
-
<?php if ( isset($this->settings['display_number']) ) { ?>
|
49 |
-
<tr class="invoice-number">
|
50 |
-
<th><?php _e( 'Invoice Number:', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
51 |
-
<td><?php $this->invoice_number(); ?></td>
|
52 |
-
</tr>
|
53 |
-
<?php } ?>
|
54 |
-
<?php if ( isset($this->settings['display_date']) ) { ?>
|
55 |
-
<tr class="invoice-date">
|
56 |
-
<th><?php _e( 'Invoice Date:', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
57 |
-
<td><?php $this->invoice_date(); ?></td>
|
58 |
-
</tr>
|
59 |
-
<?php } ?>
|
60 |
-
<tr class="order-number">
|
61 |
-
<th><?php _e( 'Order Number:', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
62 |
-
<td><?php $this->order_number(); ?></td>
|
63 |
-
</tr>
|
64 |
-
<tr class="order-date">
|
65 |
-
<th><?php _e( 'Order Date:', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
66 |
-
<td><?php $this->order_date(); ?></td>
|
67 |
-
</tr>
|
68 |
-
<tr class="payment-method">
|
69 |
-
<th><?php _e( 'Payment Method:', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
70 |
-
<td><?php $this->payment_method(); ?></td>
|
71 |
-
</tr>
|
72 |
-
<?php do_action( 'wpo_wcpdf_after_order_data', $this->type, $this->order ); ?>
|
73 |
-
</table>
|
74 |
-
</td>
|
75 |
-
</tr>
|
76 |
-
</table>
|
77 |
-
|
78 |
-
<?php do_action( 'wpo_wcpdf_before_order_details', $this->type, $this->order ); ?>
|
79 |
-
|
80 |
-
<table class="order-details">
|
81 |
-
<thead>
|
82 |
-
<tr>
|
83 |
-
<th class="product"><?php _e('Product', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
84 |
-
<th class="quantity"><?php _e('Quantity', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
85 |
-
<th class="price"><?php _e('Price', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
86 |
-
</tr>
|
87 |
-
</thead>
|
88 |
-
<tbody>
|
89 |
-
<?php $items = $this->get_order_items(); if( sizeof( $items ) > 0 ) : foreach( $items as $item_id => $item ) : ?>
|
90 |
-
<tr class="<?php echo apply_filters( 'wpo_wcpdf_item_row_class', $item_id, $this->type, $this->order, $item_id ); ?>">
|
91 |
-
<td class="product">
|
92 |
-
<?php $description_label = __( 'Description', 'woocommerce-pdf-invoices-packing-slips' ); // registering alternate label translation ?>
|
93 |
-
<span class="item-name"><?php echo $item['name']; ?></span>
|
94 |
-
<?php do_action( 'wpo_wcpdf_before_item_meta', $this->type, $item, $this->order ); ?>
|
95 |
-
<span class="item-meta"><?php echo $item['meta']; ?></span>
|
96 |
-
<dl class="meta">
|
97 |
-
<?php $description_label = __( 'SKU', 'woocommerce-pdf-invoices-packing-slips' ); // registering alternate label translation ?>
|
98 |
-
<?php if( !empty( $item['sku'] ) ) : ?><dt class="sku"><?php _e( 'SKU:', 'woocommerce-pdf-invoices-packing-slips' ); ?></dt><dd class="sku"><?php echo $item['sku']; ?></dd><?php endif; ?>
|
99 |
-
<?php if( !empty( $item['weight'] ) ) : ?><dt class="weight"><?php _e( 'Weight:', 'woocommerce-pdf-invoices-packing-slips' ); ?></dt><dd class="weight"><?php echo $item['weight']; ?><?php echo get_option('woocommerce_weight_unit'); ?></dd><?php endif; ?>
|
100 |
-
</dl>
|
101 |
-
<?php do_action( 'wpo_wcpdf_after_item_meta', $this->type, $item, $this->order ); ?>
|
102 |
-
</td>
|
103 |
-
<td class="quantity"><?php echo $item['quantity']; ?></td>
|
104 |
-
<td class="price"><?php echo $item['order_price']; ?></td>
|
105 |
-
</tr>
|
106 |
-
<?php endforeach; endif; ?>
|
107 |
-
</tbody>
|
108 |
-
<tfoot>
|
109 |
-
<tr class="no-borders">
|
110 |
-
<td class="no-borders">
|
111 |
-
<div class="customer-notes">
|
112 |
-
<?php do_action( 'wpo_wcpdf_before_customer_notes', $this->type, $this->order ); ?>
|
113 |
-
<?php if ( $this->get_shipping_notes() ) : ?>
|
114 |
-
<h3><?php _e( 'Customer Notes', 'woocommerce-pdf-invoices-packing-slips' ); ?></h3>
|
115 |
-
<?php $this->shipping_notes(); ?>
|
116 |
-
<?php endif; ?>
|
117 |
-
<?php do_action( 'wpo_wcpdf_after_customer_notes', $this->type, $this->order ); ?>
|
118 |
-
</div>
|
119 |
-
</td>
|
120 |
-
<td class="no-borders" colspan="2">
|
121 |
-
<table class="totals">
|
122 |
-
<tfoot>
|
123 |
-
<?php foreach( $this->get_woocommerce_totals() as $key => $total ) : ?>
|
124 |
-
<tr class="<?php echo $key; ?>">
|
125 |
-
<td class="no-borders"></td>
|
126 |
-
<th class="description"><?php echo $total['label']; ?></th>
|
127 |
-
<td class="price"><span class="totals-price"><?php echo $total['value']; ?></span></td>
|
128 |
-
</tr>
|
129 |
-
<?php endforeach; ?>
|
130 |
-
</tfoot>
|
131 |
-
</table>
|
132 |
-
</td>
|
133 |
-
</tr>
|
134 |
-
</tfoot>
|
135 |
-
</table>
|
136 |
-
|
137 |
-
<?php do_action( 'wpo_wcpdf_after_order_details', $this->type, $this->order ); ?>
|
138 |
-
|
139 |
-
<?php if ( $this->get_footer() ): ?>
|
140 |
-
<div id="footer">
|
141 |
-
<?php $this->footer(); ?>
|
142 |
-
</div><!-- #letter-footer -->
|
143 |
-
<?php endif; ?>
|
144 |
-
<?php do_action( 'wpo_wcpdf_after_document', $this->type, $this->order ); ?>
|
1 |
+
<?php do_action( 'wpo_wcpdf_before_document', $this->type, $this->order ); ?>
|
2 |
+
|
3 |
+
<table class="head container">
|
4 |
+
<tr>
|
5 |
+
<td class="header">
|
6 |
+
<?php
|
7 |
+
if( $this->has_header_logo() ) {
|
8 |
+
$this->header_logo();
|
9 |
+
} else {
|
10 |
+
echo $this->get_title();
|
11 |
+
}
|
12 |
+
?>
|
13 |
+
</td>
|
14 |
+
<td class="shop-info">
|
15 |
+
<div class="shop-name"><h3><?php $this->shop_name(); ?></h3></div>
|
16 |
+
<div class="shop-address"><?php $this->shop_address(); ?></div>
|
17 |
+
</td>
|
18 |
+
</tr>
|
19 |
+
</table>
|
20 |
+
|
21 |
+
<h1 class="document-type-label">
|
22 |
+
<?php if( $this->has_header_logo() ) echo $this->get_title(); ?>
|
23 |
+
</h1>
|
24 |
+
|
25 |
+
<?php do_action( 'wpo_wcpdf_after_document_label', $this->type, $this->order ); ?>
|
26 |
+
|
27 |
+
<table class="order-data-addresses">
|
28 |
+
<tr>
|
29 |
+
<td class="address billing-address">
|
30 |
+
<!-- <h3><?php _e( 'Billing Address:', 'woocommerce-pdf-invoices-packing-slips' ); ?></h3> -->
|
31 |
+
<?php $this->billing_address(); ?>
|
32 |
+
<?php if ( isset($this->settings['display_email']) ) { ?>
|
33 |
+
<div class="billing-email"><?php $this->billing_email(); ?></div>
|
34 |
+
<?php } ?>
|
35 |
+
<?php if ( isset($this->settings['display_phone']) ) { ?>
|
36 |
+
<div class="billing-phone"><?php $this->billing_phone(); ?></div>
|
37 |
+
<?php } ?>
|
38 |
+
</td>
|
39 |
+
<td class="address shipping-address">
|
40 |
+
<?php if ( isset($this->settings['display_shipping_address']) && $this->ships_to_different_address()) { ?>
|
41 |
+
<h3><?php _e( 'Ship To:', 'woocommerce-pdf-invoices-packing-slips' ); ?></h3>
|
42 |
+
<?php $this->shipping_address(); ?>
|
43 |
+
<?php } ?>
|
44 |
+
</td>
|
45 |
+
<td class="order-data">
|
46 |
+
<table>
|
47 |
+
<?php do_action( 'wpo_wcpdf_before_order_data', $this->type, $this->order ); ?>
|
48 |
+
<?php if ( isset($this->settings['display_number']) ) { ?>
|
49 |
+
<tr class="invoice-number">
|
50 |
+
<th><?php _e( 'Invoice Number:', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
51 |
+
<td><?php $this->invoice_number(); ?></td>
|
52 |
+
</tr>
|
53 |
+
<?php } ?>
|
54 |
+
<?php if ( isset($this->settings['display_date']) ) { ?>
|
55 |
+
<tr class="invoice-date">
|
56 |
+
<th><?php _e( 'Invoice Date:', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
57 |
+
<td><?php $this->invoice_date(); ?></td>
|
58 |
+
</tr>
|
59 |
+
<?php } ?>
|
60 |
+
<tr class="order-number">
|
61 |
+
<th><?php _e( 'Order Number:', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
62 |
+
<td><?php $this->order_number(); ?></td>
|
63 |
+
</tr>
|
64 |
+
<tr class="order-date">
|
65 |
+
<th><?php _e( 'Order Date:', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
66 |
+
<td><?php $this->order_date(); ?></td>
|
67 |
+
</tr>
|
68 |
+
<tr class="payment-method">
|
69 |
+
<th><?php _e( 'Payment Method:', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
70 |
+
<td><?php $this->payment_method(); ?></td>
|
71 |
+
</tr>
|
72 |
+
<?php do_action( 'wpo_wcpdf_after_order_data', $this->type, $this->order ); ?>
|
73 |
+
</table>
|
74 |
+
</td>
|
75 |
+
</tr>
|
76 |
+
</table>
|
77 |
+
|
78 |
+
<?php do_action( 'wpo_wcpdf_before_order_details', $this->type, $this->order ); ?>
|
79 |
+
|
80 |
+
<table class="order-details">
|
81 |
+
<thead>
|
82 |
+
<tr>
|
83 |
+
<th class="product"><?php _e('Product', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
84 |
+
<th class="quantity"><?php _e('Quantity', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
85 |
+
<th class="price"><?php _e('Price', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
86 |
+
</tr>
|
87 |
+
</thead>
|
88 |
+
<tbody>
|
89 |
+
<?php $items = $this->get_order_items(); if( sizeof( $items ) > 0 ) : foreach( $items as $item_id => $item ) : ?>
|
90 |
+
<tr class="<?php echo apply_filters( 'wpo_wcpdf_item_row_class', $item_id, $this->type, $this->order, $item_id ); ?>">
|
91 |
+
<td class="product">
|
92 |
+
<?php $description_label = __( 'Description', 'woocommerce-pdf-invoices-packing-slips' ); // registering alternate label translation ?>
|
93 |
+
<span class="item-name"><?php echo $item['name']; ?></span>
|
94 |
+
<?php do_action( 'wpo_wcpdf_before_item_meta', $this->type, $item, $this->order ); ?>
|
95 |
+
<span class="item-meta"><?php echo $item['meta']; ?></span>
|
96 |
+
<dl class="meta">
|
97 |
+
<?php $description_label = __( 'SKU', 'woocommerce-pdf-invoices-packing-slips' ); // registering alternate label translation ?>
|
98 |
+
<?php if( !empty( $item['sku'] ) ) : ?><dt class="sku"><?php _e( 'SKU:', 'woocommerce-pdf-invoices-packing-slips' ); ?></dt><dd class="sku"><?php echo $item['sku']; ?></dd><?php endif; ?>
|
99 |
+
<?php if( !empty( $item['weight'] ) ) : ?><dt class="weight"><?php _e( 'Weight:', 'woocommerce-pdf-invoices-packing-slips' ); ?></dt><dd class="weight"><?php echo $item['weight']; ?><?php echo get_option('woocommerce_weight_unit'); ?></dd><?php endif; ?>
|
100 |
+
</dl>
|
101 |
+
<?php do_action( 'wpo_wcpdf_after_item_meta', $this->type, $item, $this->order ); ?>
|
102 |
+
</td>
|
103 |
+
<td class="quantity"><?php echo $item['quantity']; ?></td>
|
104 |
+
<td class="price"><?php echo $item['order_price']; ?></td>
|
105 |
+
</tr>
|
106 |
+
<?php endforeach; endif; ?>
|
107 |
+
</tbody>
|
108 |
+
<tfoot>
|
109 |
+
<tr class="no-borders">
|
110 |
+
<td class="no-borders">
|
111 |
+
<div class="customer-notes">
|
112 |
+
<?php do_action( 'wpo_wcpdf_before_customer_notes', $this->type, $this->order ); ?>
|
113 |
+
<?php if ( $this->get_shipping_notes() ) : ?>
|
114 |
+
<h3><?php _e( 'Customer Notes', 'woocommerce-pdf-invoices-packing-slips' ); ?></h3>
|
115 |
+
<?php $this->shipping_notes(); ?>
|
116 |
+
<?php endif; ?>
|
117 |
+
<?php do_action( 'wpo_wcpdf_after_customer_notes', $this->type, $this->order ); ?>
|
118 |
+
</div>
|
119 |
+
</td>
|
120 |
+
<td class="no-borders" colspan="2">
|
121 |
+
<table class="totals">
|
122 |
+
<tfoot>
|
123 |
+
<?php foreach( $this->get_woocommerce_totals() as $key => $total ) : ?>
|
124 |
+
<tr class="<?php echo $key; ?>">
|
125 |
+
<td class="no-borders"></td>
|
126 |
+
<th class="description"><?php echo $total['label']; ?></th>
|
127 |
+
<td class="price"><span class="totals-price"><?php echo $total['value']; ?></span></td>
|
128 |
+
</tr>
|
129 |
+
<?php endforeach; ?>
|
130 |
+
</tfoot>
|
131 |
+
</table>
|
132 |
+
</td>
|
133 |
+
</tr>
|
134 |
+
</tfoot>
|
135 |
+
</table>
|
136 |
+
|
137 |
+
<?php do_action( 'wpo_wcpdf_after_order_details', $this->type, $this->order ); ?>
|
138 |
+
|
139 |
+
<?php if ( $this->get_footer() ): ?>
|
140 |
+
<div id="footer">
|
141 |
+
<?php $this->footer(); ?>
|
142 |
+
</div><!-- #letter-footer -->
|
143 |
+
<?php endif; ?>
|
144 |
+
<?php do_action( 'wpo_wcpdf_after_document', $this->type, $this->order ); ?>
|
templates/Simple/packing-slip.php
CHANGED
@@ -1,113 +1,113 @@
|
|
1 |
-
<?php do_action( 'wpo_wcpdf_before_document', $this->type, $this->order ); ?>
|
2 |
-
|
3 |
-
<table class="head container">
|
4 |
-
<tr>
|
5 |
-
<td class="header">
|
6 |
-
<?php
|
7 |
-
if( $this->has_header_logo() ) {
|
8 |
-
$this->header_logo();
|
9 |
-
} else {
|
10 |
-
echo $this->get_title();
|
11 |
-
}
|
12 |
-
?>
|
13 |
-
</td>
|
14 |
-
<td class="shop-info">
|
15 |
-
<div class="shop-name"><h3><?php $this->shop_name(); ?></h3></div>
|
16 |
-
<div class="shop-address"><?php $this->shop_address(); ?></div>
|
17 |
-
</td>
|
18 |
-
</tr>
|
19 |
-
</table>
|
20 |
-
|
21 |
-
<h1 class="document-type-label">
|
22 |
-
<?php if( $this->has_header_logo() ) echo $this->get_title(); ?>
|
23 |
-
</h1>
|
24 |
-
|
25 |
-
<?php do_action( 'wpo_wcpdf_after_document_label', $this->type, $this->order ); ?>
|
26 |
-
|
27 |
-
<table class="order-data-addresses">
|
28 |
-
<tr>
|
29 |
-
<td class="address shipping-address">
|
30 |
-
<!-- <h3><?php _e( 'Shipping Address:', 'woocommerce-pdf-invoices-packing-slips' ); ?></h3> -->
|
31 |
-
<?php $this->shipping_address(); ?>
|
32 |
-
<?php if ( isset($this->settings['display_email']) ) { ?>
|
33 |
-
<div class="billing-email"><?php $this->billing_email(); ?></div>
|
34 |
-
<?php } ?>
|
35 |
-
<?php if ( isset($this->settings['display_phone']) ) { ?>
|
36 |
-
<div class="billing-phone"><?php $this->billing_phone(); ?></div>
|
37 |
-
<?php } ?>
|
38 |
-
</td>
|
39 |
-
<td class="address billing-address">
|
40 |
-
<?php if ( isset($this->settings['display_billing_address']) && $this->ships_to_different_address()) { ?>
|
41 |
-
<h3><?php _e( 'Billing Address:', 'woocommerce-pdf-invoices-packing-slips' ); ?></h3>
|
42 |
-
<?php $this->billing_address(); ?>
|
43 |
-
<?php } ?>
|
44 |
-
</td>
|
45 |
-
<td class="order-data">
|
46 |
-
<table>
|
47 |
-
<?php do_action( 'wpo_wcpdf_before_order_data', $this->type, $this->order ); ?>
|
48 |
-
<tr class="order-number">
|
49 |
-
<th><?php _e( 'Order Number:', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
50 |
-
<td><?php $this->order_number(); ?></td>
|
51 |
-
</tr>
|
52 |
-
<tr class="order-date">
|
53 |
-
<th><?php _e( 'Order Date:', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
54 |
-
<td><?php $this->order_date(); ?></td>
|
55 |
-
</tr>
|
56 |
-
<tr class="shipping-method">
|
57 |
-
<th><?php _e( 'Shipping Method:', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
58 |
-
<td><?php $this->shipping_method(); ?></td>
|
59 |
-
</tr>
|
60 |
-
<?php do_action( 'wpo_wcpdf_after_order_data', $this->type, $this->order ); ?>
|
61 |
-
</table>
|
62 |
-
</td>
|
63 |
-
</tr>
|
64 |
-
</table>
|
65 |
-
|
66 |
-
<?php do_action( 'wpo_wcpdf_before_order_details', $this->type, $this->order ); ?>
|
67 |
-
|
68 |
-
<table class="order-details">
|
69 |
-
<thead>
|
70 |
-
<tr>
|
71 |
-
<th class="product"><?php _e('Product', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
72 |
-
<th class="quantity"><?php _e('Quantity', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
73 |
-
</tr>
|
74 |
-
</thead>
|
75 |
-
<tbody>
|
76 |
-
<?php $items = $this->get_order_items(); if( sizeof( $items ) > 0 ) : foreach( $items as $item_id => $item ) : ?>
|
77 |
-
<tr class="<?php echo apply_filters( 'wpo_wcpdf_item_row_class', $item_id, $this->type, $this->order, $item_id ); ?>">
|
78 |
-
<td class="product">
|
79 |
-
<?php $description_label = __( 'Description', 'woocommerce-pdf-invoices-packing-slips' ); // registering alternate label translation ?>
|
80 |
-
<span class="item-name"><?php echo $item['name']; ?></span>
|
81 |
-
<?php do_action( 'wpo_wcpdf_before_item_meta', $this->type, $item, $this->order ); ?>
|
82 |
-
<span class="item-meta"><?php echo $item['meta']; ?></span>
|
83 |
-
<dl class="meta">
|
84 |
-
<?php $description_label = __( 'SKU', 'woocommerce-pdf-invoices-packing-slips' ); // registering alternate label translation ?>
|
85 |
-
<?php if( !empty( $item['sku'] ) ) : ?><dt class="sku"><?php _e( 'SKU:', 'woocommerce-pdf-invoices-packing-slips' ); ?></dt><dd class="sku"><?php echo $item['sku']; ?></dd><?php endif; ?>
|
86 |
-
<?php if( !empty( $item['weight'] ) ) : ?><dt class="weight"><?php _e( 'Weight:', 'woocommerce-pdf-invoices-packing-slips' ); ?></dt><dd class="weight"><?php echo $item['weight']; ?><?php echo get_option('woocommerce_weight_unit'); ?></dd><?php endif; ?>
|
87 |
-
</dl>
|
88 |
-
<?php do_action( 'wpo_wcpdf_after_item_meta', $this->type, $item, $this->order ); ?>
|
89 |
-
</td>
|
90 |
-
<td class="quantity"><?php echo $item['quantity']; ?></td>
|
91 |
-
</tr>
|
92 |
-
<?php endforeach; endif; ?>
|
93 |
-
</tbody>
|
94 |
-
</table>
|
95 |
-
|
96 |
-
<?php do_action( 'wpo_wcpdf_after_order_details', $this->type, $this->order ); ?>
|
97 |
-
|
98 |
-
<?php do_action( 'wpo_wcpdf_before_customer_notes', $this->type, $this->order ); ?>
|
99 |
-
<div class="customer-notes">
|
100 |
-
<?php if ( $this->get_shipping_notes() ) : ?>
|
101 |
-
<h3><?php _e( 'Customer Notes', 'woocommerce-pdf-invoices-packing-slips' ); ?></h3>
|
102 |
-
<?php $this->shipping_notes(); ?>
|
103 |
-
<?php endif; ?>
|
104 |
-
</div>
|
105 |
-
<?php do_action( 'wpo_wcpdf_after_customer_notes', $this->type, $this->order ); ?>
|
106 |
-
|
107 |
-
<?php if ( $this->get_footer() ): ?>
|
108 |
-
<div id="footer">
|
109 |
-
<?php $this->footer(); ?>
|
110 |
-
</div><!-- #letter-footer -->
|
111 |
-
<?php endif; ?>
|
112 |
-
|
113 |
<?php do_action( 'wpo_wcpdf_after_document', $this->type, $this->order ); ?>
|
1 |
+
<?php do_action( 'wpo_wcpdf_before_document', $this->type, $this->order ); ?>
|
2 |
+
|
3 |
+
<table class="head container">
|
4 |
+
<tr>
|
5 |
+
<td class="header">
|
6 |
+
<?php
|
7 |
+
if( $this->has_header_logo() ) {
|
8 |
+
$this->header_logo();
|
9 |
+
} else {
|
10 |
+
echo $this->get_title();
|
11 |
+
}
|
12 |
+
?>
|
13 |
+
</td>
|
14 |
+
<td class="shop-info">
|
15 |
+
<div class="shop-name"><h3><?php $this->shop_name(); ?></h3></div>
|
16 |
+
<div class="shop-address"><?php $this->shop_address(); ?></div>
|
17 |
+
</td>
|
18 |
+
</tr>
|
19 |
+
</table>
|
20 |
+
|
21 |
+
<h1 class="document-type-label">
|
22 |
+
<?php if( $this->has_header_logo() ) echo $this->get_title(); ?>
|
23 |
+
</h1>
|
24 |
+
|
25 |
+
<?php do_action( 'wpo_wcpdf_after_document_label', $this->type, $this->order ); ?>
|
26 |
+
|
27 |
+
<table class="order-data-addresses">
|
28 |
+
<tr>
|
29 |
+
<td class="address shipping-address">
|
30 |
+
<!-- <h3><?php _e( 'Shipping Address:', 'woocommerce-pdf-invoices-packing-slips' ); ?></h3> -->
|
31 |
+
<?php $this->shipping_address(); ?>
|
32 |
+
<?php if ( isset($this->settings['display_email']) ) { ?>
|
33 |
+
<div class="billing-email"><?php $this->billing_email(); ?></div>
|
34 |
+
<?php } ?>
|
35 |
+
<?php if ( isset($this->settings['display_phone']) ) { ?>
|
36 |
+
<div class="billing-phone"><?php $this->billing_phone(); ?></div>
|
37 |
+
<?php } ?>
|
38 |
+
</td>
|
39 |
+
<td class="address billing-address">
|
40 |
+
<?php if ( isset($this->settings['display_billing_address']) && $this->ships_to_different_address()) { ?>
|
41 |
+
<h3><?php _e( 'Billing Address:', 'woocommerce-pdf-invoices-packing-slips' ); ?></h3>
|
42 |
+
<?php $this->billing_address(); ?>
|
43 |
+
<?php } ?>
|
44 |
+
</td>
|
45 |
+
<td class="order-data">
|
46 |
+
<table>
|
47 |
+
<?php do_action( 'wpo_wcpdf_before_order_data', $this->type, $this->order ); ?>
|
48 |
+
<tr class="order-number">
|
49 |
+
<th><?php _e( 'Order Number:', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
50 |
+
<td><?php $this->order_number(); ?></td>
|
51 |
+
</tr>
|
52 |
+
<tr class="order-date">
|
53 |
+
<th><?php _e( 'Order Date:', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
54 |
+
<td><?php $this->order_date(); ?></td>
|
55 |
+
</tr>
|
56 |
+
<tr class="shipping-method">
|
57 |
+
<th><?php _e( 'Shipping Method:', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
58 |
+
<td><?php $this->shipping_method(); ?></td>
|
59 |
+
</tr>
|
60 |
+
<?php do_action( 'wpo_wcpdf_after_order_data', $this->type, $this->order ); ?>
|
61 |
+
</table>
|
62 |
+
</td>
|
63 |
+
</tr>
|
64 |
+
</table>
|
65 |
+
|
66 |
+
<?php do_action( 'wpo_wcpdf_before_order_details', $this->type, $this->order ); ?>
|
67 |
+
|
68 |
+
<table class="order-details">
|
69 |
+
<thead>
|
70 |
+
<tr>
|
71 |
+
<th class="product"><?php _e('Product', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
72 |
+
<th class="quantity"><?php _e('Quantity', 'woocommerce-pdf-invoices-packing-slips' ); ?></th>
|
73 |
+
</tr>
|
74 |
+
</thead>
|
75 |
+
<tbody>
|
76 |
+
<?php $items = $this->get_order_items(); if( sizeof( $items ) > 0 ) : foreach( $items as $item_id => $item ) : ?>
|
77 |
+
<tr class="<?php echo apply_filters( 'wpo_wcpdf_item_row_class', $item_id, $this->type, $this->order, $item_id ); ?>">
|
78 |
+
<td class="product">
|
79 |
+
<?php $description_label = __( 'Description', 'woocommerce-pdf-invoices-packing-slips' ); // registering alternate label translation ?>
|
80 |
+
<span class="item-name"><?php echo $item['name']; ?></span>
|
81 |
+
<?php do_action( 'wpo_wcpdf_before_item_meta', $this->type, $item, $this->order ); ?>
|
82 |
+
<span class="item-meta"><?php echo $item['meta']; ?></span>
|
83 |
+
<dl class="meta">
|
84 |
+
<?php $description_label = __( 'SKU', 'woocommerce-pdf-invoices-packing-slips' ); // registering alternate label translation ?>
|
85 |
+
<?php if( !empty( $item['sku'] ) ) : ?><dt class="sku"><?php _e( 'SKU:', 'woocommerce-pdf-invoices-packing-slips' ); ?></dt><dd class="sku"><?php echo $item['sku']; ?></dd><?php endif; ?>
|
86 |
+
<?php if( !empty( $item['weight'] ) ) : ?><dt class="weight"><?php _e( 'Weight:', 'woocommerce-pdf-invoices-packing-slips' ); ?></dt><dd class="weight"><?php echo $item['weight']; ?><?php echo get_option('woocommerce_weight_unit'); ?></dd><?php endif; ?>
|
87 |
+
</dl>
|
88 |
+
<?php do_action( 'wpo_wcpdf_after_item_meta', $this->type, $item, $this->order ); ?>
|
89 |
+
</td>
|
90 |
+
<td class="quantity"><?php echo $item['quantity']; ?></td>
|
91 |
+
</tr>
|
92 |
+
<?php endforeach; endif; ?>
|
93 |
+
</tbody>
|
94 |
+
</table>
|
95 |
+
|
96 |
+
<?php do_action( 'wpo_wcpdf_after_order_details', $this->type, $this->order ); ?>
|
97 |
+
|
98 |
+
<?php do_action( 'wpo_wcpdf_before_customer_notes', $this->type, $this->order ); ?>
|
99 |
+
<div class="customer-notes">
|
100 |
+
<?php if ( $this->get_shipping_notes() ) : ?>
|
101 |
+
<h3><?php _e( 'Customer Notes', 'woocommerce-pdf-invoices-packing-slips' ); ?></h3>
|
102 |
+
<?php $this->shipping_notes(); ?>
|
103 |
+
<?php endif; ?>
|
104 |
+
</div>
|
105 |
+
<?php do_action( 'wpo_wcpdf_after_customer_notes', $this->type, $this->order ); ?>
|
106 |
+
|
107 |
+
<?php if ( $this->get_footer() ): ?>
|
108 |
+
<div id="footer">
|
109 |
+
<?php $this->footer(); ?>
|
110 |
+
</div><!-- #letter-footer -->
|
111 |
+
<?php endif; ?>
|
112 |
+
|
113 |
<?php do_action( 'wpo_wcpdf_after_document', $this->type, $this->order ); ?>
|
vendor/autoload.php
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
// autoload.php @generated by Composer
|
4 |
-
|
5 |
-
require_once __DIR__ . '/composer/autoload_real.php';
|
6 |
-
|
7 |
-
return ComposerAutoloaderInit50d65aade31e72e54f6b791559872608::getLoader();
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload.php @generated by Composer
|
4 |
+
|
5 |
+
require_once __DIR__ . '/composer/autoload_real.php';
|
6 |
+
|
7 |
+
return ComposerAutoloaderInit50d65aade31e72e54f6b791559872608::getLoader();
|
vendor/composer/ClassLoader.php
CHANGED
@@ -1,445 +1,445 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/*
|
4 |
-
* This file is part of Composer.
|
5 |
-
*
|
6 |
-
* (c) Nils Adermann <naderman@naderman.de>
|
7 |
-
* Jordi Boggiano <j.boggiano@seld.be>
|
8 |
-
*
|
9 |
-
* For the full copyright and license information, please view the LICENSE
|
10 |
-
* file that was distributed with this source code.
|
11 |
-
*/
|
12 |
-
|
13 |
-
namespace Composer\Autoload;
|
14 |
-
|
15 |
-
/**
|
16 |
-
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
17 |
-
*
|
18 |
-
* $loader = new \Composer\Autoload\ClassLoader();
|
19 |
-
*
|
20 |
-
* // register classes with namespaces
|
21 |
-
* $loader->add('Symfony\Component', __DIR__.'/component');
|
22 |
-
* $loader->add('Symfony', __DIR__.'/framework');
|
23 |
-
*
|
24 |
-
* // activate the autoloader
|
25 |
-
* $loader->register();
|
26 |
-
*
|
27 |
-
* // to enable searching the include path (eg. for PEAR packages)
|
28 |
-
* $loader->setUseIncludePath(true);
|
29 |
-
*
|
30 |
-
* In this example, if you try to use a class in the Symfony\Component
|
31 |
-
* namespace or one of its children (Symfony\Component\Console for instance),
|
32 |
-
* the autoloader will first look for the class under the component/
|
33 |
-
* directory, and it will then fallback to the framework/ directory if not
|
34 |
-
* found before giving up.
|
35 |
-
*
|
36 |
-
* This class is loosely based on the Symfony UniversalClassLoader.
|
37 |
-
*
|
38 |
-
* @author Fabien Potencier <fabien@symfony.com>
|
39 |
-
* @author Jordi Boggiano <j.boggiano@seld.be>
|
40 |
-
* @see http://www.php-fig.org/psr/psr-0/
|
41 |
-
* @see http://www.php-fig.org/psr/psr-4/
|
42 |
-
*/
|
43 |
-
class ClassLoader
|
44 |
-
{
|
45 |
-
// PSR-4
|
46 |
-
private $prefixLengthsPsr4 = array();
|
47 |
-
private $prefixDirsPsr4 = array();
|
48 |
-
private $fallbackDirsPsr4 = array();
|
49 |
-
|
50 |
-
// PSR-0
|
51 |
-
private $prefixesPsr0 = array();
|
52 |
-
private $fallbackDirsPsr0 = array();
|
53 |
-
|
54 |
-
private $useIncludePath = false;
|
55 |
-
private $classMap = array();
|
56 |
-
private $classMapAuthoritative = false;
|
57 |
-
private $missingClasses = array();
|
58 |
-
private $apcuPrefix;
|
59 |
-
|
60 |
-
public function getPrefixes()
|
61 |
-
{
|
62 |
-
if (!empty($this->prefixesPsr0)) {
|
63 |
-
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
64 |
-
}
|
65 |
-
|
66 |
-
return array();
|
67 |
-
}
|
68 |
-
|
69 |
-
public function getPrefixesPsr4()
|
70 |
-
{
|
71 |
-
return $this->prefixDirsPsr4;
|
72 |
-
}
|
73 |
-
|
74 |
-
public function getFallbackDirs()
|
75 |
-
{
|
76 |
-
return $this->fallbackDirsPsr0;
|
77 |
-
}
|
78 |
-
|
79 |
-
public function getFallbackDirsPsr4()
|
80 |
-
{
|
81 |
-
return $this->fallbackDirsPsr4;
|
82 |
-
}
|
83 |
-
|
84 |
-
public function getClassMap()
|
85 |
-
{
|
86 |
-
return $this->classMap;
|
87 |
-
}
|
88 |
-
|
89 |
-
/**
|
90 |
-
* @param array $classMap Class to filename map
|
91 |
-
*/
|
92 |
-
public function addClassMap(array $classMap)
|
93 |
-
{
|
94 |
-
if ($this->classMap) {
|
95 |
-
$this->classMap = array_merge($this->classMap, $classMap);
|
96 |
-
} else {
|
97 |
-
$this->classMap = $classMap;
|
98 |
-
}
|
99 |
-
}
|
100 |
-
|
101 |
-
/**
|
102 |
-
* Registers a set of PSR-0 directories for a given prefix, either
|
103 |
-
* appending or prepending to the ones previously set for this prefix.
|
104 |
-
*
|
105 |
-
* @param string $prefix The prefix
|
106 |
-
* @param array|string $paths The PSR-0 root directories
|
107 |
-
* @param bool $prepend Whether to prepend the directories
|
108 |
-
*/
|
109 |
-
public function add($prefix, $paths, $prepend = false)
|
110 |
-
{
|
111 |
-
if (!$prefix) {
|
112 |
-
if ($prepend) {
|
113 |
-
$this->fallbackDirsPsr0 = array_merge(
|
114 |
-
(array) $paths,
|
115 |
-
$this->fallbackDirsPsr0
|
116 |
-
);
|
117 |
-
} else {
|
118 |
-
$this->fallbackDirsPsr0 = array_merge(
|
119 |
-
$this->fallbackDirsPsr0,
|
120 |
-
(array) $paths
|
121 |
-
);
|
122 |
-
}
|
123 |
-
|
124 |
-
return;
|
125 |
-
}
|
126 |
-
|
127 |
-
$first = $prefix[0];
|
128 |
-
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
129 |
-
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
130 |
-
|
131 |
-
return;
|
132 |
-
}
|
133 |
-
if ($prepend) {
|
134 |
-
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
135 |
-
(array) $paths,
|
136 |
-
$this->prefixesPsr0[$first][$prefix]
|
137 |
-
);
|
138 |
-
} else {
|
139 |
-
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
140 |
-
$this->prefixesPsr0[$first][$prefix],
|
141 |
-
(array) $paths
|
142 |
-
);
|
143 |
-
}
|
144 |
-
}
|
145 |
-
|
146 |
-
/**
|
147 |
-
* Registers a set of PSR-4 directories for a given namespace, either
|
148 |
-
* appending or prepending to the ones previously set for this namespace.
|
149 |
-
*
|
150 |
-
* @param string $prefix The prefix/namespace, with trailing '\\'
|
151 |
-
* @param array|string $paths The PSR-4 base directories
|
152 |
-
* @param bool $prepend Whether to prepend the directories
|
153 |
-
*
|
154 |
-
* @throws \InvalidArgumentException
|
155 |
-
*/
|
156 |
-
public function addPsr4($prefix, $paths, $prepend = false)
|
157 |
-
{
|
158 |
-
if (!$prefix) {
|
159 |
-
// Register directories for the root namespace.
|
160 |
-
if ($prepend) {
|
161 |
-
$this->fallbackDirsPsr4 = array_merge(
|
162 |
-
(array) $paths,
|
163 |
-
$this->fallbackDirsPsr4
|
164 |
-
);
|
165 |
-
} else {
|
166 |
-
$this->fallbackDirsPsr4 = array_merge(
|
167 |
-
$this->fallbackDirsPsr4,
|
168 |
-
(array) $paths
|
169 |
-
);
|
170 |
-
}
|
171 |
-
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
172 |
-
// Register directories for a new namespace.
|
173 |
-
$length = strlen($prefix);
|
174 |
-
if ('\\' !== $prefix[$length - 1]) {
|
175 |
-
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
176 |
-
}
|
177 |
-
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
178 |
-
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
179 |
-
} elseif ($prepend) {
|
180 |
-
// Prepend directories for an already registered namespace.
|
181 |
-
$this->prefixDirsPsr4[$prefix] = array_merge(
|
182 |
-
(array) $paths,
|
183 |
-
$this->prefixDirsPsr4[$prefix]
|
184 |
-
);
|
185 |
-
} else {
|
186 |
-
// Append directories for an already registered namespace.
|
187 |
-
$this->prefixDirsPsr4[$prefix] = array_merge(
|
188 |
-
$this->prefixDirsPsr4[$prefix],
|
189 |
-
(array) $paths
|
190 |
-
);
|
191 |
-
}
|
192 |
-
}
|
193 |
-
|
194 |
-
/**
|
195 |
-
* Registers a set of PSR-0 directories for a given prefix,
|
196 |
-
* replacing any others previously set for this prefix.
|
197 |
-
*
|
198 |
-
* @param string $prefix The prefix
|
199 |
-
* @param array|string $paths The PSR-0 base directories
|
200 |
-
*/
|
201 |
-
public function set($prefix, $paths)
|
202 |
-
{
|
203 |
-
if (!$prefix) {
|
204 |
-
$this->fallbackDirsPsr0 = (array) $paths;
|
205 |
-
} else {
|
206 |
-
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
207 |
-
}
|
208 |
-
}
|
209 |
-
|
210 |
-
/**
|
211 |
-
* Registers a set of PSR-4 directories for a given namespace,
|
212 |
-
* replacing any others previously set for this namespace.
|
213 |
-
*
|
214 |
-
* @param string $prefix The prefix/namespace, with trailing '\\'
|
215 |
-
* @param array|string $paths The PSR-4 base directories
|
216 |
-
*
|
217 |
-
* @throws \InvalidArgumentException
|
218 |
-
*/
|
219 |
-
public function setPsr4($prefix, $paths)
|
220 |
-
{
|
221 |
-
if (!$prefix) {
|
222 |
-
$this->fallbackDirsPsr4 = (array) $paths;
|
223 |
-
} else {
|
224 |
-
$length = strlen($prefix);
|
225 |
-
if ('\\' !== $prefix[$length - 1]) {
|
226 |
-
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
227 |
-
}
|
228 |
-
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
229 |
-
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
230 |
-
}
|
231 |
-
}
|
232 |
-
|
233 |
-
/**
|
234 |
-
* Turns on searching the include path for class files.
|
235 |
-
*
|
236 |
-
* @param bool $useIncludePath
|
237 |
-
*/
|
238 |
-
public function setUseIncludePath($useIncludePath)
|
239 |
-
{
|
240 |
-
$this->useIncludePath = $useIncludePath;
|
241 |
-
}
|
242 |
-
|
243 |
-
/**
|
244 |
-
* Can be used to check if the autoloader uses the include path to check
|
245 |
-
* for classes.
|
246 |
-
*
|
247 |
-
* @return bool
|
248 |
-
*/
|
249 |
-
public function getUseIncludePath()
|
250 |
-
{
|
251 |
-
return $this->useIncludePath;
|
252 |
-
}
|
253 |
-
|
254 |
-
/**
|
255 |
-
* Turns off searching the prefix and fallback directories for classes
|
256 |
-
* that have not been registered with the class map.
|
257 |
-
*
|
258 |
-
* @param bool $classMapAuthoritative
|
259 |
-
*/
|
260 |
-
public function setClassMapAuthoritative($classMapAuthoritative)
|
261 |
-
{
|
262 |
-
$this->classMapAuthoritative = $classMapAuthoritative;
|
263 |
-
}
|
264 |
-
|
265 |
-
/**
|
266 |
-
* Should class lookup fail if not found in the current class map?
|
267 |
-
*
|
268 |
-
* @return bool
|
269 |
-
*/
|
270 |
-
public function isClassMapAuthoritative()
|
271 |
-
{
|
272 |
-
return $this->classMapAuthoritative;
|
273 |
-
}
|
274 |
-
|
275 |
-
/**
|
276 |
-
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
277 |
-
*
|
278 |
-
* @param string|null $apcuPrefix
|
279 |
-
*/
|
280 |
-
public function setApcuPrefix($apcuPrefix)
|
281 |
-
{
|
282 |
-
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
|
283 |
-
}
|
284 |
-
|
285 |
-
/**
|
286 |
-
* The APCu prefix in use, or null if APCu caching is not enabled.
|
287 |
-
*
|
288 |
-
* @return string|null
|
289 |
-
*/
|
290 |
-
public function getApcuPrefix()
|
291 |
-
{
|
292 |
-
return $this->apcuPrefix;
|
293 |
-
}
|
294 |
-
|
295 |
-
/**
|
296 |
-
* Registers this instance as an autoloader.
|
297 |
-
*
|
298 |
-
* @param bool $prepend Whether to prepend the autoloader or not
|
299 |
-
*/
|
300 |
-
public function register($prepend = false)
|
301 |
-
{
|
302 |
-
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
303 |
-
}
|
304 |
-
|
305 |
-
/**
|
306 |
-
* Unregisters this instance as an autoloader.
|
307 |
-
*/
|
308 |
-
public function unregister()
|
309 |
-
{
|
310 |
-
spl_autoload_unregister(array($this, 'loadClass'));
|
311 |
-
}
|
312 |
-
|
313 |
-
/**
|
314 |
-
* Loads the given class or interface.
|
315 |
-
*
|
316 |
-
* @param string $class The name of the class
|
317 |
-
* @return bool|null True if loaded, null otherwise
|
318 |
-
*/
|
319 |
-
public function loadClass($class)
|
320 |
-
{
|
321 |
-
if ($file = $this->findFile($class)) {
|
322 |
-
includeFile($file);
|
323 |
-
|
324 |
-
return true;
|
325 |
-
}
|
326 |
-
}
|
327 |
-
|
328 |
-
/**
|
329 |
-
* Finds the path to the file where the class is defined.
|
330 |
-
*
|
331 |
-
* @param string $class The name of the class
|
332 |
-
*
|
333 |
-
* @return string|false The path if found, false otherwise
|
334 |
-
*/
|
335 |
-
public function findFile($class)
|
336 |
-
{
|
337 |
-
// class map lookup
|
338 |
-
if (isset($this->classMap[$class])) {
|
339 |
-
return $this->classMap[$class];
|
340 |
-
}
|
341 |
-
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
342 |
-
return false;
|
343 |
-
}
|
344 |
-
if (null !== $this->apcuPrefix) {
|
345 |
-
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
346 |
-
if ($hit) {
|
347 |
-
return $file;
|
348 |
-
}
|
349 |
-
}
|
350 |
-
|
351 |
-
$file = $this->findFileWithExtension($class, '.php');
|
352 |
-
|
353 |
-
// Search for Hack files if we are running on HHVM
|
354 |
-
if (false === $file && defined('HHVM_VERSION')) {
|
355 |
-
$file = $this->findFileWithExtension($class, '.hh');
|
356 |
-
}
|
357 |
-
|
358 |
-
if (null !== $this->apcuPrefix) {
|
359 |
-
apcu_add($this->apcuPrefix.$class, $file);
|
360 |
-
}
|
361 |
-
|
362 |
-
if (false === $file) {
|
363 |
-
// Remember that this class does not exist.
|
364 |
-
$this->missingClasses[$class] = true;
|
365 |
-
}
|
366 |
-
|
367 |
-
return $file;
|
368 |
-
}
|
369 |
-
|
370 |
-
private function findFileWithExtension($class, $ext)
|
371 |
-
{
|
372 |
-
// PSR-4 lookup
|
373 |
-
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
374 |
-
|
375 |
-
$first = $class[0];
|
376 |
-
if (isset($this->prefixLengthsPsr4[$first])) {
|
377 |
-
$subPath = $class;
|
378 |
-
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
379 |
-
$subPath = substr($subPath, 0, $lastPos);
|
380 |
-
$search = $subPath.'\\';
|
381 |
-
if (isset($this->prefixDirsPsr4[$search])) {
|
382 |
-
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
383 |
-
$length = $this->prefixLengthsPsr4[$first][$search];
|
384 |
-
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
|
385 |
-
return $file;
|
386 |
-
}
|
387 |
-
}
|
388 |
-
}
|
389 |
-
}
|
390 |
-
}
|
391 |
-
|
392 |
-
// PSR-4 fallback dirs
|
393 |
-
foreach ($this->fallbackDirsPsr4 as $dir) {
|
394 |
-
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
395 |
-
return $file;
|
396 |
-
}
|
397 |
-
}
|
398 |
-
|
399 |
-
// PSR-0 lookup
|
400 |
-
if (false !== $pos = strrpos($class, '\\')) {
|
401 |
-
// namespaced class name
|
402 |
-
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
403 |
-
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
404 |
-
} else {
|
405 |
-
// PEAR-like class name
|
406 |
-
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
407 |
-
}
|
408 |
-
|
409 |
-
if (isset($this->prefixesPsr0[$first])) {
|
410 |
-
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
411 |
-
if (0 === strpos($class, $prefix)) {
|
412 |
-
foreach ($dirs as $dir) {
|
413 |
-
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
414 |
-
return $file;
|
415 |
-
}
|
416 |
-
}
|
417 |
-
}
|
418 |
-
}
|
419 |
-
}
|
420 |
-
|
421 |
-
// PSR-0 fallback dirs
|
422 |
-
foreach ($this->fallbackDirsPsr0 as $dir) {
|
423 |
-
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
424 |
-
return $file;
|
425 |
-
}
|
426 |
-
}
|
427 |
-
|
428 |
-
// PSR-0 include paths.
|
429 |
-
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
430 |
-
return $file;
|
431 |
-
}
|
432 |
-
|
433 |
-
return false;
|
434 |
-
}
|
435 |
-
}
|
436 |
-
|
437 |
-
/**
|
438 |
-
* Scope isolated include.
|
439 |
-
*
|
440 |
-
* Prevents access to $this/self from included files.
|
441 |
-
*/
|
442 |
-
function includeFile($file)
|
443 |
-
{
|
444 |
-
include $file;
|
445 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Composer.
|
5 |
+
*
|
6 |
+
* (c) Nils Adermann <naderman@naderman.de>
|
7 |
+
* Jordi Boggiano <j.boggiano@seld.be>
|
8 |
+
*
|
9 |
+
* For the full copyright and license information, please view the LICENSE
|
10 |
+
* file that was distributed with this source code.
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace Composer\Autoload;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
17 |
+
*
|
18 |
+
* $loader = new \Composer\Autoload\ClassLoader();
|
19 |
+
*
|
20 |
+
* // register classes with namespaces
|
21 |
+
* $loader->add('Symfony\Component', __DIR__.'/component');
|
22 |
+
* $loader->add('Symfony', __DIR__.'/framework');
|
23 |
+
*
|
24 |
+
* // activate the autoloader
|
25 |
+
* $loader->register();
|
26 |
+
*
|
27 |
+
* // to enable searching the include path (eg. for PEAR packages)
|
28 |
+
* $loader->setUseIncludePath(true);
|
29 |
+
*
|
30 |
+
* In this example, if you try to use a class in the Symfony\Component
|
31 |
+
* namespace or one of its children (Symfony\Component\Console for instance),
|
32 |
+
* the autoloader will first look for the class under the component/
|
33 |
+
* directory, and it will then fallback to the framework/ directory if not
|
34 |
+
* found before giving up.
|
35 |
+
*
|
36 |
+
* This class is loosely based on the Symfony UniversalClassLoader.
|
37 |
+
*
|
38 |
+
* @author Fabien Potencier <fabien@symfony.com>
|
39 |
+
* @author Jordi Boggiano <j.boggiano@seld.be>
|
40 |
+
* @see http://www.php-fig.org/psr/psr-0/
|
41 |
+
* @see http://www.php-fig.org/psr/psr-4/
|
42 |
+
*/
|
43 |
+
class ClassLoader
|
44 |
+
{
|
45 |
+
// PSR-4
|
46 |
+
private $prefixLengthsPsr4 = array();
|
47 |
+
private $prefixDirsPsr4 = array();
|
48 |
+
private $fallbackDirsPsr4 = array();
|
49 |
+
|
50 |
+
// PSR-0
|
51 |
+
private $prefixesPsr0 = array();
|
52 |
+
private $fallbackDirsPsr0 = array();
|
53 |
+
|
54 |
+
private $useIncludePath = false;
|
55 |
+
private $classMap = array();
|
56 |
+
private $classMapAuthoritative = false;
|
57 |
+
private $missingClasses = array();
|
58 |
+
private $apcuPrefix;
|
59 |
+
|
60 |
+
public function getPrefixes()
|
61 |
+
{
|
62 |
+
if (!empty($this->prefixesPsr0)) {
|
63 |
+
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
64 |
+
}
|
65 |
+
|
66 |
+
return array();
|
67 |
+
}
|
68 |
+
|
69 |
+
public function getPrefixesPsr4()
|
70 |
+
{
|
71 |
+
return $this->prefixDirsPsr4;
|
72 |
+
}
|
73 |
+
|
74 |
+
public function getFallbackDirs()
|
75 |
+
{
|
76 |
+
return $this->fallbackDirsPsr0;
|
77 |
+
}
|
78 |
+
|
79 |
+
public function getFallbackDirsPsr4()
|
80 |
+
{
|
81 |
+
return $this->fallbackDirsPsr4;
|
82 |
+
}
|
83 |
+
|
84 |
+
public function getClassMap()
|
85 |
+
{
|
86 |
+
return $this->classMap;
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* @param array $classMap Class to filename map
|
91 |
+
*/
|
92 |
+
public function addClassMap(array $classMap)
|
93 |
+
{
|
94 |
+
if ($this->classMap) {
|
95 |
+
$this->classMap = array_merge($this->classMap, $classMap);
|
96 |
+
} else {
|
97 |
+
$this->classMap = $classMap;
|
98 |
+
}
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Registers a set of PSR-0 directories for a given prefix, either
|
103 |
+
* appending or prepending to the ones previously set for this prefix.
|
104 |
+
*
|
105 |
+
* @param string $prefix The prefix
|
106 |
+
* @param array|string $paths The PSR-0 root directories
|
107 |
+
* @param bool $prepend Whether to prepend the directories
|
108 |
+
*/
|
109 |
+
public function add($prefix, $paths, $prepend = false)
|
110 |
+
{
|
111 |
+
if (!$prefix) {
|
112 |
+
if ($prepend) {
|
113 |
+
$this->fallbackDirsPsr0 = array_merge(
|
114 |
+
(array) $paths,
|
115 |
+
$this->fallbackDirsPsr0
|
116 |
+
);
|
117 |
+
} else {
|
118 |
+
$this->fallbackDirsPsr0 = array_merge(
|
119 |
+
$this->fallbackDirsPsr0,
|
120 |
+
(array) $paths
|
121 |
+
);
|
122 |
+
}
|
123 |
+
|
124 |
+
return;
|
125 |
+
}
|
126 |
+
|
127 |
+
$first = $prefix[0];
|
128 |
+
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
129 |
+
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
130 |
+
|
131 |
+
return;
|
132 |
+
}
|
133 |
+
if ($prepend) {
|
134 |
+
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
135 |
+
(array) $paths,
|
136 |
+
$this->prefixesPsr0[$first][$prefix]
|
137 |
+
);
|
138 |
+
} else {
|
139 |
+
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
140 |
+
$this->prefixesPsr0[$first][$prefix],
|
141 |
+
(array) $paths
|
142 |
+
);
|
143 |
+
}
|
144 |
+
}
|
145 |
+
|
146 |
+
/**
|
147 |
+
* Registers a set of PSR-4 directories for a given namespace, either
|
148 |
+
* appending or prepending to the ones previously set for this namespace.
|
149 |
+
*
|
150 |
+
* @param string $prefix The prefix/namespace, with trailing '\\'
|
151 |
+
* @param array|string $paths The PSR-4 base directories
|
152 |
+
* @param bool $prepend Whether to prepend the directories
|
153 |
+
*
|
154 |
+
* @throws \InvalidArgumentException
|
155 |
+
*/
|
156 |
+
public function addPsr4($prefix, $paths, $prepend = false)
|
157 |
+
{
|
158 |
+
if (!$prefix) {
|
159 |
+
// Register directories for the root namespace.
|
160 |
+
if ($prepend) {
|
161 |
+
$this->fallbackDirsPsr4 = array_merge(
|
162 |
+
(array) $paths,
|
163 |
+
$this->fallbackDirsPsr4
|
164 |
+
);
|
165 |
+
} else {
|
166 |
+
$this->fallbackDirsPsr4 = array_merge(
|
167 |
+
$this->fallbackDirsPsr4,
|
168 |
+
(array) $paths
|
169 |
+
);
|
170 |
+
}
|
171 |
+
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
172 |
+
// Register directories for a new namespace.
|
173 |
+
$length = strlen($prefix);
|
174 |
+
if ('\\' !== $prefix[$length - 1]) {
|
175 |
+
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
176 |
+
}
|
177 |
+
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
178 |
+
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
179 |
+
} elseif ($prepend) {
|
180 |
+
// Prepend directories for an already registered namespace.
|
181 |
+
$this->prefixDirsPsr4[$prefix] = array_merge(
|
182 |
+
(array) $paths,
|
183 |
+
$this->prefixDirsPsr4[$prefix]
|
184 |
+
);
|
185 |
+
} else {
|
186 |
+
// Append directories for an already registered namespace.
|
187 |
+
$this->prefixDirsPsr4[$prefix] = array_merge(
|
188 |
+
$this->prefixDirsPsr4[$prefix],
|
189 |
+
(array) $paths
|
190 |
+
);
|
191 |
+
}
|
192 |
+
}
|
193 |
+
|
194 |
+
/**
|
195 |
+
* Registers a set of PSR-0 directories for a given prefix,
|
196 |
+
* replacing any others previously set for this prefix.
|
197 |
+
*
|
198 |
+
* @param string $prefix The prefix
|
199 |
+
* @param array|string $paths The PSR-0 base directories
|
200 |
+
*/
|
201 |
+
public function set($prefix, $paths)
|
202 |
+
{
|
203 |
+
if (!$prefix) {
|
204 |
+
$this->fallbackDirsPsr0 = (array) $paths;
|
205 |
+
} else {
|
206 |
+
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
207 |
+
}
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Registers a set of PSR-4 directories for a given namespace,
|
212 |
+
* replacing any others previously set for this namespace.
|
213 |
+
*
|
214 |
+
* @param string $prefix The prefix/namespace, with trailing '\\'
|
215 |
+
* @param array|string $paths The PSR-4 base directories
|
216 |
+
*
|
217 |
+
* @throws \InvalidArgumentException
|
218 |
+
*/
|
219 |
+
public function setPsr4($prefix, $paths)
|
220 |
+
{
|
221 |
+
if (!$prefix) {
|
222 |
+
$this->fallbackDirsPsr4 = (array) $paths;
|
223 |
+
} else {
|
224 |
+
$length = strlen($prefix);
|
225 |
+
if ('\\' !== $prefix[$length - 1]) {
|
226 |
+
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
227 |
+
}
|
228 |
+
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
229 |
+
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
230 |
+
}
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Turns on searching the include path for class files.
|
235 |
+
*
|
236 |
+
* @param bool $useIncludePath
|
237 |
+
*/
|
238 |
+
public function setUseIncludePath($useIncludePath)
|
239 |
+
{
|
240 |
+
$this->useIncludePath = $useIncludePath;
|
241 |
+
}
|
242 |
+
|
243 |
+
/**
|
244 |
+
* Can be used to check if the autoloader uses the include path to check
|
245 |
+
* for classes.
|
246 |
+
*
|
247 |
+
* @return bool
|
248 |
+
*/
|
249 |
+
public function getUseIncludePath()
|
250 |
+
{
|
251 |
+
return $this->useIncludePath;
|
252 |
+
}
|
253 |
+
|
254 |
+
/**
|
255 |
+
* Turns off searching the prefix and fallback directories for classes
|
256 |
+
* that have not been registered with the class map.
|
257 |
+
*
|
258 |
+
* @param bool $classMapAuthoritative
|
259 |
+
*/
|
260 |
+
public function setClassMapAuthoritative($classMapAuthoritative)
|
261 |
+
{
|
262 |
+
$this->classMapAuthoritative = $classMapAuthoritative;
|
263 |
+
}
|
264 |
+
|
265 |
+
/**
|
266 |
+
* Should class lookup fail if not found in the current class map?
|
267 |
+
*
|
268 |
+
* @return bool
|
269 |
+
*/
|
270 |
+
public function isClassMapAuthoritative()
|
271 |
+
{
|
272 |
+
return $this->classMapAuthoritative;
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
277 |
+
*
|
278 |
+
* @param string|null $apcuPrefix
|
279 |
+
*/
|
280 |
+
public function setApcuPrefix($apcuPrefix)
|
281 |
+
{
|
282 |
+
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
|
283 |
+
}
|
284 |
+
|
285 |
+
/**
|
286 |
+
* The APCu prefix in use, or null if APCu caching is not enabled.
|
287 |
+
*
|
288 |
+
* @return string|null
|
289 |
+
*/
|
290 |
+
public function getApcuPrefix()
|
291 |
+
{
|
292 |
+
return $this->apcuPrefix;
|
293 |
+
}
|
294 |
+
|
295 |
+
/**
|
296 |
+
* Registers this instance as an autoloader.
|
297 |
+
*
|
298 |
+
* @param bool $prepend Whether to prepend the autoloader or not
|
299 |
+
*/
|
300 |
+
public function register($prepend = false)
|
301 |
+
{
|
302 |
+
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
303 |
+
}
|
304 |
+
|
305 |
+
/**
|
306 |
+
* Unregisters this instance as an autoloader.
|
307 |
+
*/
|
308 |
+
public function unregister()
|
309 |
+
{
|
310 |
+
spl_autoload_unregister(array($this, 'loadClass'));
|
311 |
+
}
|
312 |
+
|
313 |
+
/**
|
314 |
+
* Loads the given class or interface.
|
315 |
+
*
|
316 |
+
* @param string $class The name of the class
|
317 |
+
* @return bool|null True if loaded, null otherwise
|
318 |
+
*/
|
319 |
+
public function loadClass($class)
|
320 |
+
{
|
321 |
+
if ($file = $this->findFile($class)) {
|
322 |
+
includeFile($file);
|
323 |
+
|
324 |
+
return true;
|
325 |
+
}
|
326 |
+
}
|
327 |
+
|
328 |
+
/**
|
329 |
+
* Finds the path to the file where the class is defined.
|
330 |
+
*
|
331 |
+
* @param string $class The name of the class
|
332 |
+
*
|
333 |
+
* @return string|false The path if found, false otherwise
|
334 |
+
*/
|
335 |
+
public function findFile($class)
|
336 |
+
{
|
337 |
+
// class map lookup
|
338 |
+
if (isset($this->classMap[$class])) {
|
339 |
+
return $this->classMap[$class];
|
340 |
+
}
|
341 |
+
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
342 |
+
return false;
|
343 |
+
}
|
344 |
+
if (null !== $this->apcuPrefix) {
|
345 |
+
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
346 |
+
if ($hit) {
|
347 |
+
return $file;
|
348 |
+
}
|
349 |
+
}
|
350 |
+
|
351 |
+
$file = $this->findFileWithExtension($class, '.php');
|
352 |
+
|
353 |
+
// Search for Hack files if we are running on HHVM
|
354 |
+
if (false === $file && defined('HHVM_VERSION')) {
|
355 |
+
$file = $this->findFileWithExtension($class, '.hh');
|
356 |
+
}
|
357 |
+
|
358 |
+
if (null !== $this->apcuPrefix) {
|
359 |
+
apcu_add($this->apcuPrefix.$class, $file);
|
360 |
+
}
|
361 |
+
|
362 |
+
if (false === $file) {
|
363 |
+
// Remember that this class does not exist.
|
364 |
+
$this->missingClasses[$class] = true;
|
365 |
+
}
|
366 |
+
|
367 |
+
return $file;
|
368 |
+
}
|
369 |
+
|
370 |
+
private function findFileWithExtension($class, $ext)
|
371 |
+
{
|
372 |
+
// PSR-4 lookup
|
373 |
+
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
374 |
+
|
375 |
+
$first = $class[0];
|
376 |
+
if (isset($this->prefixLengthsPsr4[$first])) {
|
377 |
+
$subPath = $class;
|
378 |
+
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
379 |
+
$subPath = substr($subPath, 0, $lastPos);
|
380 |
+
$search = $subPath.'\\';
|
381 |
+
if (isset($this->prefixDirsPsr4[$search])) {
|
382 |
+
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
383 |
+
$length = $this->prefixLengthsPsr4[$first][$search];
|
384 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
|
385 |
+
return $file;
|
386 |
+
}
|
387 |
+
}
|
388 |
+
}
|
389 |
+
}
|
390 |
+
}
|
391 |
+
|
392 |
+
// PSR-4 fallback dirs
|
393 |
+
foreach ($this->fallbackDirsPsr4 as $dir) {
|
394 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
395 |
+
return $file;
|
396 |
+
}
|
397 |
+
}
|
398 |
+
|
399 |
+
// PSR-0 lookup
|
400 |
+
if (false !== $pos = strrpos($class, '\\')) {
|
401 |
+
// namespaced class name
|
402 |
+
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
403 |
+
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
404 |
+
} else {
|
405 |
+
// PEAR-like class name
|
406 |
+
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
407 |
+
}
|
408 |
+
|
409 |
+
if (isset($this->prefixesPsr0[$first])) {
|
410 |
+
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
411 |
+
if (0 === strpos($class, $prefix)) {
|
412 |
+
foreach ($dirs as $dir) {
|
413 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
414 |
+
return $file;
|
415 |
+
}
|
416 |
+
}
|
417 |
+
}
|
418 |
+
}
|
419 |
+
}
|
420 |
+
|
421 |
+
// PSR-0 fallback dirs
|
422 |
+
foreach ($this->fallbackDirsPsr0 as $dir) {
|
423 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
424 |
+
return $file;
|
425 |
+
}
|
426 |
+
}
|
427 |
+
|
428 |
+
// PSR-0 include paths.
|
429 |
+
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
430 |
+
return $file;
|
431 |
+
}
|
432 |
+
|
433 |
+
return false;
|
434 |
+
}
|
435 |
+
}
|
436 |
+
|
437 |
+
/**
|
438 |
+
* Scope isolated include.
|
439 |
+
*
|
440 |
+
* Prevents access to $this/self from included files.
|
441 |
+
*/
|
442 |
+
function includeFile($file)
|
443 |
+
{
|
444 |
+
include $file;
|
445 |
+
}
|
vendor/composer/LICENSE
CHANGED
@@ -1,21 +1,21 @@
|
|
1 |
-
|
2 |
-
Copyright (c) Nils Adermann, Jordi Boggiano
|
3 |
-
|
4 |
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5 |
-
of this software and associated documentation files (the "Software"), to deal
|
6 |
-
in the Software without restriction, including without limitation the rights
|
7 |
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8 |
-
copies of the Software, and to permit persons to whom the Software is furnished
|
9 |
-
to do so, subject to the following conditions:
|
10 |
-
|
11 |
-
The above copyright notice and this permission notice shall be included in all
|
12 |
-
copies or substantial portions of the Software.
|
13 |
-
|
14 |
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15 |
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16 |
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17 |
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18 |
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19 |
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20 |
-
THE SOFTWARE.
|
21 |
-
|
1 |
+
|
2 |
+
Copyright (c) Nils Adermann, Jordi Boggiano
|
3 |
+
|
4 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5 |
+
of this software and associated documentation files (the "Software"), to deal
|
6 |
+
in the Software without restriction, including without limitation the rights
|
7 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8 |
+
copies of the Software, and to permit persons to whom the Software is furnished
|
9 |
+
to do so, subject to the following conditions:
|
10 |
+
|
11 |
+
The above copyright notice and this permission notice shall be included in all
|
12 |
+
copies or substantial portions of the Software.
|
13 |
+
|
14 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20 |
+
THE SOFTWARE.
|
21 |
+
|
vendor/composer/autoload_real.php
CHANGED
@@ -1,52 +1,52 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
// autoload_real.php @generated by Composer
|
4 |
-
|
5 |
-
class ComposerAutoloaderInit50d65aade31e72e54f6b791559872608
|
6 |
-
{
|
7 |
-
private static $loader;
|
8 |
-
|
9 |
-
public static function loadClassLoader($class)
|
10 |
-
{
|
11 |
-
if ('Composer\Autoload\ClassLoader' === $class) {
|
12 |
-
require __DIR__ . '/ClassLoader.php';
|
13 |
-
}
|
14 |
-
}
|
15 |
-
|
16 |
-
public static function getLoader()
|
17 |
-
{
|
18 |
-
if (null !== self::$loader) {
|
19 |
-
return self::$loader;
|
20 |
-
}
|
21 |
-
|
22 |
-
spl_autoload_register(array('ComposerAutoloaderInit50d65aade31e72e54f6b791559872608', 'loadClassLoader'), true, true);
|
23 |
-
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
24 |
-
spl_autoload_unregister(array('ComposerAutoloaderInit50d65aade31e72e54f6b791559872608', 'loadClassLoader'));
|
25 |
-
|
26 |
-
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
27 |
-
if ($useStaticLoader) {
|
28 |
-
require_once __DIR__ . '/autoload_static.php';
|
29 |
-
|
30 |
-
call_user_func(\Composer\Autoload\ComposerStaticInit50d65aade31e72e54f6b791559872608::getInitializer($loader));
|
31 |
-
} else {
|
32 |
-
$map = require __DIR__ . '/autoload_namespaces.php';
|
33 |
-
foreach ($map as $namespace => $path) {
|
34 |
-
$loader->set($namespace, $path);
|
35 |
-
}
|
36 |
-
|
37 |
-
$map = require __DIR__ . '/autoload_psr4.php';
|
38 |
-
foreach ($map as $namespace => $path) {
|
39 |
-
$loader->setPsr4($namespace, $path);
|
40 |
-
}
|
41 |
-
|
42 |
-
$classMap = require __DIR__ . '/autoload_classmap.php';
|
43 |
-
if ($classMap) {
|
44 |
-
$loader->addClassMap($classMap);
|
45 |
-
}
|
46 |
-
}
|
47 |
-
|
48 |
-
$loader->register(true);
|
49 |
-
|
50 |
-
return $loader;
|
51 |
-
}
|
52 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_real.php @generated by Composer
|
4 |
+
|
5 |
+
class ComposerAutoloaderInit50d65aade31e72e54f6b791559872608
|
6 |
+
{
|
7 |
+
private static $loader;
|
8 |
+
|
9 |
+
public static function loadClassLoader($class)
|
10 |
+
{
|
11 |
+
if ('Composer\Autoload\ClassLoader' === $class) {
|
12 |
+
require __DIR__ . '/ClassLoader.php';
|
13 |
+
}
|
14 |
+
}
|
15 |
+
|
16 |
+
public static function getLoader()
|
17 |
+
{
|
18 |
+
if (null !== self::$loader) {
|
19 |
+
return self::$loader;
|
20 |
+
}
|
21 |
+
|
22 |
+
spl_autoload_register(array('ComposerAutoloaderInit50d65aade31e72e54f6b791559872608', 'loadClassLoader'), true, true);
|
23 |
+
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
24 |
+
spl_autoload_unregister(array('ComposerAutoloaderInit50d65aade31e72e54f6b791559872608', 'loadClassLoader'));
|
25 |
+
|
26 |
+
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
27 |
+
if ($useStaticLoader) {
|
28 |
+
require_once __DIR__ . '/autoload_static.php';
|
29 |
+
|
30 |
+
call_user_func(\Composer\Autoload\ComposerStaticInit50d65aade31e72e54f6b791559872608::getInitializer($loader));
|
31 |
+
} else {
|
32 |
+
$map = require __DIR__ . '/autoload_namespaces.php';
|
33 |
+
foreach ($map as $namespace => $path) {
|
34 |
+
$loader->set($namespace, $path);
|
35 |
+
}
|
36 |
+
|
37 |
+
$map = require __DIR__ . '/autoload_psr4.php';
|
38 |
+
foreach ($map as $namespace => $path) {
|
39 |
+
$loader->setPsr4($namespace, $path);
|
40 |
+
}
|
41 |
+
|
42 |
+
$classMap = require __DIR__ . '/autoload_classmap.php';
|
43 |
+
if ($classMap) {
|
44 |
+
$loader->addClassMap($classMap);
|
45 |
+
}
|
46 |
+
}
|
47 |
+
|
48 |
+
$loader->register(true);
|
49 |
+
|
50 |
+
return $loader;
|
51 |
+
}
|
52 |
+
}
|
vendor/composer/installed.json
CHANGED
@@ -1,193 +1,193 @@
|
|
1 |
-
[
|
2 |
-
{
|
3 |
-
"name": "sabberworm/php-css-parser",
|
4 |
-
"version": "8.1.0",
|
5 |
-
"version_normalized": "8.1.0.0",
|
6 |
-
"source": {
|
7 |
-
"type": "git",
|
8 |
-
"url": "https://github.com/sabberworm/PHP-CSS-Parser.git",
|
9 |
-
"reference": "850cbbcbe7fbb155387a151ea562897a67e242ef"
|
10 |
-
},
|
11 |
-
"dist": {
|
12 |
-
"type": "zip",
|
13 |
-
"url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/850cbbcbe7fbb155387a151ea562897a67e242ef",
|
14 |
-
"reference": "850cbbcbe7fbb155387a151ea562897a67e242ef",
|
15 |
-
"shasum": ""
|
16 |
-
},
|
17 |
-
"require": {
|
18 |
-
"php": ">=5.3.2"
|
19 |
-
},
|
20 |
-
"require-dev": {
|
21 |
-
"phpunit/phpunit": "*"
|
22 |
-
},
|
23 |
-
"time": "2016-07-19T19:14:21+00:00",
|
24 |
-
"type": "library",
|
25 |
-
"installation-source": "dist",
|
26 |
-
"autoload": {
|
27 |
-
"psr-0": {
|
28 |
-
"Sabberworm\\CSS": "lib/"
|
29 |
-
}
|
30 |
-
},
|
31 |
-
"notification-url": "https://packagist.org/downloads/",
|
32 |
-
"license": [
|
33 |
-
"MIT"
|
34 |
-
],
|
35 |
-
"authors": [
|
36 |
-
{
|
37 |
-
"name": "Raphael Schweikert"
|
38 |
-
}
|
39 |
-
],
|
40 |
-
"description": "Parser for CSS Files written in PHP",
|
41 |
-
"homepage": "http://www.sabberworm.com/blog/2010/6/10/php-css-parser",
|
42 |
-
"keywords": [
|
43 |
-
"css",
|
44 |
-
"parser",
|
45 |
-
"stylesheet"
|
46 |
-
]
|
47 |
-
},
|
48 |
-
{
|
49 |
-
"name": "phenx/php-svg-lib",
|
50 |
-
"version": "v0.3",
|
51 |
-
"version_normalized": "0.3.0.0",
|
52 |
-
"source": {
|
53 |
-
"type": "git",
|
54 |
-
"url": "https://github.com/PhenX/php-svg-lib.git",
|
55 |
-
"reference": "a85f7fe9fe08d093a4a8583cdd306b553ff918aa"
|
56 |
-
},
|
57 |
-
"dist": {
|
58 |
-
"type": "zip",
|
59 |
-
"url": "https://api.github.com/repos/PhenX/php-svg-lib/zipball/a85f7fe9fe08d093a4a8583cdd306b553ff918aa",
|
60 |
-
"reference": "a85f7fe9fe08d093a4a8583cdd306b553ff918aa",
|
61 |
-
"shasum": ""
|
62 |
-
},
|
63 |
-
"require": {
|
64 |
-
"sabberworm/php-css-parser": "8.1.*"
|
65 |
-
},
|
66 |
-
"require-dev": {
|
67 |
-
"phpunit/phpunit": "~5.0"
|
68 |
-
},
|
69 |
-
"time": "2017-05-24T10:07:27+00:00",
|
70 |
-
"type": "library",
|
71 |
-
"installation-source": "dist",
|
72 |
-
"autoload": {
|
73 |
-
"psr-0": {
|
74 |
-
"Svg\\": "src/"
|
75 |
-
}
|
76 |
-
},
|
77 |
-
"notification-url": "https://packagist.org/downloads/",
|
78 |
-
"license": [
|
79 |
-
"LGPL-3.0"
|
80 |
-
],
|
81 |
-
"authors": [
|
82 |
-
{
|
83 |
-
"name": "Fabien Ménager",
|
84 |
-
"email": "fabien.menager@gmail.com"
|
85 |
-
}
|
86 |
-
],
|
87 |
-
"description": "A library to read, parse and export to PDF SVG files.",
|
88 |
-
"homepage": "https://github.com/PhenX/php-svg-lib"
|
89 |
-
},
|
90 |
-
{
|
91 |
-
"name": "phenx/php-font-lib",
|
92 |
-
"version": "0.5.1",
|
93 |
-
"version_normalized": "0.5.1.0",
|
94 |
-
"source": {
|
95 |
-
"type": "git",
|
96 |
-
"url": "https://github.com/PhenX/php-font-lib.git",
|
97 |
-
"reference": "760148820110a1ae0936e5cc35851e25a938bc97"
|
98 |
-
},
|
99 |
-
"dist": {
|
100 |
-
"type": "zip",
|
101 |
-
"url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/760148820110a1ae0936e5cc35851e25a938bc97",
|
102 |
-
"reference": "760148820110a1ae0936e5cc35851e25a938bc97",
|
103 |
-
"shasum": ""
|
104 |
-
},
|
105 |
-
"require-dev": {
|
106 |
-
"phpunit/phpunit": "^4.8"
|
107 |
-
},
|
108 |
-
"time": "2017-09-13T16:14:37+00:00",
|
109 |
-
"type": "library",
|
110 |
-
"installation-source": "dist",
|
111 |
-
"autoload": {
|
112 |
-
"psr-4": {
|
113 |
-
"FontLib\\": "src/FontLib"
|
114 |
-
}
|
115 |
-
},
|
116 |
-
"notification-url": "https://packagist.org/downloads/",
|
117 |
-
"license": [
|
118 |
-
"LGPL-3.0"
|
119 |
-
],
|
120 |
-
"authors": [
|
121 |
-
{
|
122 |
-
"name": "Fabien Ménager",
|
123 |
-
"email": "fabien.menager@gmail.com"
|
124 |
-
}
|
125 |
-
],
|
126 |
-
"description": "A library to read, parse, export and make subsets of different types of font files.",
|
127 |
-
"homepage": "https://github.com/PhenX/php-font-lib"
|
128 |
-
},
|
129 |
-
{
|
130 |
-
"name": "dompdf/dompdf",
|
131 |
-
"version": "v0.8.2",
|
132 |
-
"version_normalized": "0.8.2.0",
|
133 |
-
"source": {
|
134 |
-
"type": "git",
|
135 |
-
"url": "https://github.com/dompdf/dompdf.git",
|
136 |
-
"reference": "5113accd9ae5d466077cce5208dcf3fb871bf8f6"
|
137 |
-
},
|
138 |
-
"dist": {
|
139 |
-
"type": "zip",
|
140 |
-
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/5113accd9ae5d466077cce5208dcf3fb871bf8f6",
|
141 |
-
"reference": "5113accd9ae5d466077cce5208dcf3fb871bf8f6",
|
142 |
-
"shasum": ""
|
143 |
-
},
|
144 |
-
"require": {
|
145 |
-
"ext-dom": "*",
|
146 |
-
"ext-gd": "*",
|
147 |
-
"ext-mbstring": "*",
|
148 |
-
"phenx/php-font-lib": "0.5.*",
|
149 |
-
"phenx/php-svg-lib": "0.3.*",
|
150 |
-
"php": ">=5.4.0"
|
151 |
-
},
|
152 |
-
"require-dev": {
|
153 |
-
"phpunit/phpunit": "4.8.*",
|
154 |
-
"squizlabs/php_codesniffer": "2.*"
|
155 |
-
},
|
156 |
-
"time": "2017-11-26T14:49:08+00:00",
|
157 |
-
"type": "library",
|
158 |
-
"extra": {
|
159 |
-
"branch-alias": {
|
160 |
-
"dev-develop": "0.7-dev"
|
161 |
-
}
|
162 |
-
},
|
163 |
-
"installation-source": "dist",
|
164 |
-
"autoload": {
|
165 |
-
"psr-4": {
|
166 |
-
"Dompdf\\": "src/"
|
167 |
-
},
|
168 |
-
"classmap": [
|
169 |
-
"lib/"
|
170 |
-
]
|
171 |
-
},
|
172 |
-
"notification-url": "https://packagist.org/downloads/",
|
173 |
-
"license": [
|
174 |
-
"LGPL-2.1"
|
175 |
-
],
|
176 |
-
"authors": [
|
177 |
-
{
|
178 |
-
"name": "Fabien Ménager",
|
179 |
-
"email": "fabien.menager@gmail.com"
|
180 |
-
},
|
181 |
-
{
|
182 |
-
"name": "Brian Sweeney",
|
183 |
-
"email": "eclecticgeek@gmail.com"
|
184 |
-
},
|
185 |
-
{
|
186 |
-
"name": "Gabriel Bull",
|
187 |
-
"email": "me@gabrielbull.com"
|
188 |
-
}
|
189 |
-
],
|
190 |
-
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
|
191 |
-
"homepage": "https://github.com/dompdf/dompdf"
|
192 |
-
}
|
193 |
-
]
|
1 |
+
[
|
2 |
+
{
|
3 |
+
"name": "sabberworm/php-css-parser",
|
4 |
+
"version": "8.1.0",
|
5 |
+
"version_normalized": "8.1.0.0",
|
6 |
+
"source": {
|
7 |
+
"type": "git",
|
8 |
+
"url": "https://github.com/sabberworm/PHP-CSS-Parser.git",
|
9 |
+
"reference": "850cbbcbe7fbb155387a151ea562897a67e242ef"
|
10 |
+
},
|
11 |
+
"dist": {
|
12 |
+
"type": "zip",
|
13 |
+
"url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/850cbbcbe7fbb155387a151ea562897a67e242ef",
|
14 |
+
"reference": "850cbbcbe7fbb155387a151ea562897a67e242ef",
|
15 |
+
"shasum": ""
|
16 |
+
},
|
17 |
+
"require": {
|
18 |
+
"php": ">=5.3.2"
|
19 |
+
},
|
20 |
+
"require-dev": {
|
21 |
+
"phpunit/phpunit": "*"
|
22 |
+
},
|
23 |
+
"time": "2016-07-19T19:14:21+00:00",
|
24 |
+
"type": "library",
|
25 |
+
"installation-source": "dist",
|
26 |
+
"autoload": {
|
27 |
+
"psr-0": {
|
28 |
+
"Sabberworm\\CSS": "lib/"
|
29 |
+
}
|
30 |
+
},
|
31 |
+
"notification-url": "https://packagist.org/downloads/",
|
32 |
+
"license": [
|
33 |
+
"MIT"
|
34 |
+
],
|
35 |
+
"authors": [
|
36 |
+
{
|
37 |
+
"name": "Raphael Schweikert"
|
38 |
+
}
|
39 |
+
],
|
40 |
+
"description": "Parser for CSS Files written in PHP",
|
41 |
+
"homepage": "http://www.sabberworm.com/blog/2010/6/10/php-css-parser",
|
42 |
+
"keywords": [
|
43 |
+
"css",
|
44 |
+
"parser",
|
45 |
+
"stylesheet"
|
46 |
+
]
|
47 |
+
},
|
48 |
+
{
|
49 |
+
"name": "phenx/php-svg-lib",
|
50 |
+
"version": "v0.3",
|
51 |
+
"version_normalized": "0.3.0.0",
|
52 |
+
"source": {
|
53 |
+
"type": "git",
|
54 |
+
"url": "https://github.com/PhenX/php-svg-lib.git",
|
55 |
+
"reference": "a85f7fe9fe08d093a4a8583cdd306b553ff918aa"
|
56 |
+
},
|
57 |
+
"dist": {
|
58 |
+
"type": "zip",
|
59 |
+
"url": "https://api.github.com/repos/PhenX/php-svg-lib/zipball/a85f7fe9fe08d093a4a8583cdd306b553ff918aa",
|
60 |
+
"reference": "a85f7fe9fe08d093a4a8583cdd306b553ff918aa",
|
61 |
+
"shasum": ""
|
62 |
+
},
|
63 |
+
"require": {
|
64 |
+
"sabberworm/php-css-parser": "8.1.*"
|
65 |
+
},
|
66 |
+
"require-dev": {
|
67 |
+
"phpunit/phpunit": "~5.0"
|
68 |
+
},
|
69 |
+
"time": "2017-05-24T10:07:27+00:00",
|
70 |
+
"type": "library",
|
71 |
+
"installation-source": "dist",
|
72 |
+
"autoload": {
|
73 |
+
"psr-0": {
|
74 |
+
"Svg\\": "src/"
|
75 |
+
}
|
76 |
+
},
|
77 |
+
"notification-url": "https://packagist.org/downloads/",
|
78 |
+
"license": [
|
79 |
+
"LGPL-3.0"
|
80 |
+
],
|
81 |
+
"authors": [
|
82 |
+
{
|
83 |
+
"name": "Fabien Ménager",
|
84 |
+
"email": "fabien.menager@gmail.com"
|
85 |
+
}
|
86 |
+
],
|
87 |
+
"description": "A library to read, parse and export to PDF SVG files.",
|
88 |
+
"homepage": "https://github.com/PhenX/php-svg-lib"
|
89 |
+
},
|
90 |
+
{
|
91 |
+
"name": "phenx/php-font-lib",
|
92 |
+
"version": "0.5.1",
|
93 |
+
"version_normalized": "0.5.1.0",
|
94 |
+
"source": {
|
95 |
+
"type": "git",
|
96 |
+
"url": "https://github.com/PhenX/php-font-lib.git",
|
97 |
+
"reference": "760148820110a1ae0936e5cc35851e25a938bc97"
|
98 |
+
},
|
99 |
+
"dist": {
|
100 |
+
"type": "zip",
|
101 |
+
"url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/760148820110a1ae0936e5cc35851e25a938bc97",
|
102 |
+
"reference": "760148820110a1ae0936e5cc35851e25a938bc97",
|
103 |
+
"shasum": ""
|
104 |
+
},
|
105 |
+
"require-dev": {
|
106 |
+
"phpunit/phpunit": "^4.8"
|
107 |
+
},
|
108 |
+
"time": "2017-09-13T16:14:37+00:00",
|
109 |
+
"type": "library",
|
110 |
+
"installation-source": "dist",
|
111 |
+
"autoload": {
|
112 |
+
"psr-4": {
|
113 |
+
"FontLib\\": "src/FontLib"
|
114 |
+
}
|
115 |
+
},
|
116 |
+
"notification-url": "https://packagist.org/downloads/",
|
117 |
+
"license": [
|
118 |
+
"LGPL-3.0"
|
119 |
+
],
|
120 |
+
"authors": [
|
121 |
+
{
|
122 |
+
"name": "Fabien Ménager",
|
123 |
+
"email": "fabien.menager@gmail.com"
|
124 |
+
}
|
125 |
+
],
|
126 |
+
"description": "A library to read, parse, export and make subsets of different types of font files.",
|
127 |
+
"homepage": "https://github.com/PhenX/php-font-lib"
|
128 |
+
},
|
129 |
+
{
|
130 |
+
"name": "dompdf/dompdf",
|
131 |
+
"version": "v0.8.2",
|
132 |
+
"version_normalized": "0.8.2.0",
|
133 |
+
"source": {
|
134 |
+
"type": "git",
|
135 |
+
"url": "https://github.com/dompdf/dompdf.git",
|
136 |
+
"reference": "5113accd9ae5d466077cce5208dcf3fb871bf8f6"
|
137 |
+
},
|
138 |
+
"dist": {
|
139 |
+
"type": "zip",
|
140 |
+
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/5113accd9ae5d466077cce5208dcf3fb871bf8f6",
|
141 |
+
"reference": "5113accd9ae5d466077cce5208dcf3fb871bf8f6",
|
142 |
+
"shasum": ""
|
143 |
+
},
|
144 |
+
"require": {
|
145 |
+
"ext-dom": "*",
|
146 |
+
"ext-gd": "*",
|
147 |
+
"ext-mbstring": "*",
|
148 |
+
"phenx/php-font-lib": "0.5.*",
|
149 |
+
"phenx/php-svg-lib": "0.3.*",
|
150 |
+
"php": ">=5.4.0"
|
151 |
+
},
|
152 |
+
"require-dev": {
|
153 |
+
"phpunit/phpunit": "4.8.*",
|
154 |
+
"squizlabs/php_codesniffer": "2.*"
|
155 |
+
},
|
156 |
+
"time": "2017-11-26T14:49:08+00:00",
|
157 |
+
"type": "library",
|
158 |
+
"extra": {
|
159 |
+
"branch-alias": {
|
160 |
+
"dev-develop": "0.7-dev"
|
161 |
+
}
|
162 |
+
},
|
163 |
+
"installation-source": "dist",
|
164 |
+
"autoload": {
|
165 |
+
"psr-4": {
|
166 |
+
"Dompdf\\": "src/"
|
167 |
+
},
|
168 |
+
"classmap": [
|
169 |
+
"lib/"
|
170 |
+
]
|
171 |
+
},
|
172 |
+
"notification-url": "https://packagist.org/downloads/",
|
173 |
+
"license": [
|
174 |
+
"LGPL-2.1"
|
175 |
+
],
|
176 |
+
"authors": [
|
177 |
+
{
|
178 |
+
"name": "Fabien Ménager",
|
179 |
+
"email": "fabien.menager@gmail.com"
|
180 |
+
},
|
181 |
+
{
|
182 |
+
"name": "Brian Sweeney",
|
183 |
+
"email": "eclecticgeek@gmail.com"
|
184 |
+
},
|
185 |
+
{
|
186 |
+
"name": "Gabriel Bull",
|
187 |
+
"email": "me@gabrielbull.com"
|
188 |
+
}
|
189 |
+
],
|
190 |
+
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
|
191 |
+
"homepage": "https://github.com/dompdf/dompdf"
|
192 |
+
}
|
193 |
+
]
|
vendor/dompdf/dompdf/CONTRIBUTING.md
CHANGED
@@ -1,65 +1,65 @@
|
|
1 |
-
# How to contribute
|
2 |
-
|
3 |
-
- [Getting help](#getting-help)
|
4 |
-
- [Submitting bug reports](#submitting-bug-reports)
|
5 |
-
- [Contributing code](#contributing-code)
|
6 |
-
|
7 |
-
## Getting help
|
8 |
-
|
9 |
-
Community discussion, questions, and informal bug reporting is done on the
|
10 |
-
[dompdf Google group](http://groups.google.com/group/dompdf). You may also
|
11 |
-
seek help on
|
12 |
-
[StackOverflow](http://stackoverflow.com/questions/tagged/dompdf).
|
13 |
-
|
14 |
-
## Submitting bug reports
|
15 |
-
|
16 |
-
The preferred way to report bugs is to use the
|
17 |
-
[GitHub issue tracker](http://github.com/dompdf/dompdf/issues). Before
|
18 |
-
reporting a bug, read these pointers.
|
19 |
-
|
20 |
-
**Please search inside the bug tracker to see if the bug you found is not already reported.**
|
21 |
-
|
22 |
-
**Note:** The issue tracker is for *bugs* and *feature requests*, not requests for help.
|
23 |
-
Questions should be asked on the
|
24 |
-
[dompdf Google group](http://groups.google.com/group/dompdf) instead.
|
25 |
-
|
26 |
-
### Reporting bugs effectively
|
27 |
-
|
28 |
-
- dompdf is maintained by volunteers. They don't owe you anything, so be
|
29 |
-
polite. Reports with an indignant or belligerent tone tend to be moved to the
|
30 |
-
bottom of the pile.
|
31 |
-
|
32 |
-
- Include information about **the PHP version on which the problem occurred**. Even
|
33 |
-
if you tested several PHP version on different servers, and the problem occurred
|
34 |
-
in all of them, mention this fact in the bug report.
|
35 |
-
Also include the operating system it's installed on. PHP configuration can also help,
|
36 |
-
and server error logs (like Apache logs)
|
37 |
-
|
38 |
-
- Mention which release of dompdf you're using (the zip, the master branch, etc).
|
39 |
-
Preferably, try also with the current development snapshot, to ensure the
|
40 |
-
problem has not already been fixed.
|
41 |
-
|
42 |
-
- Mention very precisely what went wrong. "X is broken" is not a good bug
|
43 |
-
report. What did you expect to happen? What happened instead? Describe the
|
44 |
-
exact steps a maintainer has to take to make the problem occur. We can not
|
45 |
-
fix something that we can not observe.
|
46 |
-
|
47 |
-
- If the problem can not be reproduced in any of the demos included in the
|
48 |
-
dompdf distribution, please provide an HTML document that demonstrates
|
49 |
-
the problem. There are a few options to show us your code:
|
50 |
-
- [JS Fiddle](http://jsfiddle.net/)
|
51 |
-
- [dompdf debug helper](http://eclecticgeek.com/dompdf/debug.php) (provided by @bsweeney)
|
52 |
-
- Include the HTML/CSS inside the bug report, with
|
53 |
-
[code highlighting](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#wiki-code).
|
54 |
-
|
55 |
-
## Contributing code
|
56 |
-
|
57 |
-
- Make sure you have a [GitHub Account](https://github.com/signup/free)
|
58 |
-
- Fork [dompdf](https://github.com/dompdf/dompdf/)
|
59 |
-
([how to fork a repo](https://help.github.com/articles/fork-a-repo))
|
60 |
-
- *Make your changes on the `develop` branch* or the most appropriate feature branch. Please only patch
|
61 |
-
the master branch if you are attempting to address an urgent bug in the released code.
|
62 |
-
- Add a simple test file in `www/test/`, with a comprehensive name.
|
63 |
-
- Add a unit test in the ``test/Dompdf/Tests/`` directory.
|
64 |
-
- Submit a pull request
|
65 |
-
([how to create a pull request](https://help.github.com/articles/fork-a-repo))
|
1 |
+
# How to contribute
|
2 |
+
|
3 |
+
- [Getting help](#getting-help)
|
4 |
+
- [Submitting bug reports](#submitting-bug-reports)
|
5 |
+
- [Contributing code](#contributing-code)
|
6 |
+
|
7 |
+
## Getting help
|
8 |
+
|
9 |
+
Community discussion, questions, and informal bug reporting is done on the
|
10 |
+
[dompdf Google group](http://groups.google.com/group/dompdf). You may also
|
11 |
+
seek help on
|
12 |
+
[StackOverflow](http://stackoverflow.com/questions/tagged/dompdf).
|
13 |
+
|
14 |
+
## Submitting bug reports
|
15 |
+
|
16 |
+
The preferred way to report bugs is to use the
|
17 |
+
[GitHub issue tracker](http://github.com/dompdf/dompdf/issues). Before
|
18 |
+
reporting a bug, read these pointers.
|
19 |
+
|
20 |
+
**Please search inside the bug tracker to see if the bug you found is not already reported.**
|
21 |
+
|
22 |
+
**Note:** The issue tracker is for *bugs* and *feature requests*, not requests for help.
|
23 |
+
Questions should be asked on the
|
24 |
+
[dompdf Google group](http://groups.google.com/group/dompdf) instead.
|
25 |
+
|
26 |
+
### Reporting bugs effectively
|
27 |
+
|
28 |
+
- dompdf is maintained by volunteers. They don't owe you anything, so be
|
29 |
+
polite. Reports with an indignant or belligerent tone tend to be moved to the
|
30 |
+
bottom of the pile.
|
31 |
+
|
32 |
+
- Include information about **the PHP version on which the problem occurred**. Even
|
33 |
+
if you tested several PHP version on different servers, and the problem occurred
|
34 |
+
in all of them, mention this fact in the bug report.
|
35 |
+
Also include the operating system it's installed on. PHP configuration can also help,
|
36 |
+
and server error logs (like Apache logs)
|
37 |
+
|
38 |
+
- Mention which release of dompdf you're using (the zip, the master branch, etc).
|
39 |
+
Preferably, try also with the current development snapshot, to ensure the
|
40 |
+
problem has not already been fixed.
|
41 |
+
|
42 |
+
- Mention very precisely what went wrong. "X is broken" is not a good bug
|
43 |
+
report. What did you expect to happen? What happened instead? Describe the
|
44 |
+
exact steps a maintainer has to take to make the problem occur. We can not
|
45 |
+
fix something that we can not observe.
|
46 |
+
|
47 |
+
- If the problem can not be reproduced in any of the demos included in the
|
48 |
+
dompdf distribution, please provide an HTML document that demonstrates
|
49 |
+
the problem. There are a few options to show us your code:
|
50 |
+
- [JS Fiddle](http://jsfiddle.net/)
|
51 |
+
- [dompdf debug helper](http://eclecticgeek.com/dompdf/debug.php) (provided by @bsweeney)
|
52 |
+
- Include the HTML/CSS inside the bug report, with
|
53 |
+
[code highlighting](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#wiki-code).
|
54 |
+
|
55 |
+
## Contributing code
|
56 |
+
|
57 |
+
- Make sure you have a [GitHub Account](https://github.com/signup/free)
|
58 |
+
- Fork [dompdf](https://github.com/dompdf/dompdf/)
|
59 |
+
([how to fork a repo](https://help.github.com/articles/fork-a-repo))
|
60 |
+
- *Make your changes on the `develop` branch* or the most appropriate feature branch. Please only patch
|
61 |
+
the master branch if you are attempting to address an urgent bug in the released code.
|
62 |
+
- Add a simple test file in `www/test/`, with a comprehensive name.
|
63 |
+
- Add a unit test in the ``test/Dompdf/Tests/`` directory.
|
64 |
+
- Submit a pull request
|
65 |
+
([how to create a pull request](https://help.github.com/articles/fork-a-repo))
|
vendor/dompdf/dompdf/README.md
CHANGED
@@ -1,211 +1,211 @@
|
|
1 |
-
Dompdf
|
2 |
-
======
|
3 |
-
|
4 |
-
[![Build Status](https://travis-ci.org/dompdf/dompdf.png?branch=master)](https://travis-ci.org/dompdf/dompdf)
|
5 |
-
[![Latest Stable Version](https://poser.pugx.org/dompdf/dompdf/v/stable.png)](https://packagist.org/packages/dompdf/dompdf)
|
6 |
-
[![Total Downloads](https://poser.pugx.org/dompdf/dompdf/downloads.png)](https://packagist.org/packages/dompdf/dompdf)
|
7 |
-
[![Latest Unstable Version](https://poser.pugx.org/dompdf/dompdf/v/unstable.png)](https://packagist.org/packages/dompdf/dompdf)
|
8 |
-
[![License](https://poser.pugx.org/dompdf/dompdf/license.png)](https://packagist.org/packages/dompdf/dompdf)
|
9 |
-
|
10 |
-
**Dompdf is an HTML to PDF converter**
|
11 |
-
|
12 |
-
At its heart, dompdf is (mostly) a [CSS 2.1](http://www.w3.org/TR/CSS2/) compliant
|
13 |
-
HTML layout and rendering engine written in PHP. It is a style-driven renderer:
|
14 |
-
it will download and read external stylesheets, inline style tags, and the style
|
15 |
-
attributes of individual HTML elements. It also supports most presentational
|
16 |
-
HTML attributes.
|
17 |
-
|
18 |
-
*This document applies to the latest stable code which may not reflect the current
|
19 |
-
release. For released code please
|
20 |
-
[navigate to the appropriate tag](https://github.com/dompdf/dompdf/tags).*
|
21 |
-
|
22 |
-
----
|
23 |
-
|
24 |
-
**Check out the [demo](https://dompdf.net/examples.php) and ask any
|
25 |
-
question on [StackOverflow](http://stackoverflow.com/questions/tagged/dompdf) or
|
26 |
-
on the [Google Groups](http://groups.google.com/group/dompdf).**
|
27 |
-
|
28 |
-
Follow us on [![Twitter](http://twitter-badges.s3.amazonaws.com/twitter-a.png)](http://www.twitter.com/dompdf) or
|
29 |
-
[![Follow us on Google+](https://ssl.gstatic.com/images/icons/gplus-16.png)](https://plus.google.com/108710008521858993320?prsrc=3).
|
30 |
-
|
31 |
-
---
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
## Features
|
36 |
-
|
37 |
-
* Handles most CSS 2.1 and a few CSS3 properties, including @import, @media &
|
38 |
-
@page rules
|
39 |
-
* Supports most presentational HTML 4.0 attributes
|
40 |
-
* Supports external stylesheets, either local or through http/ftp (via
|
41 |
-
fopen-wrappers)
|
42 |
-
* Supports complex tables, including row & column spans, separate & collapsed
|
43 |
-
border models, individual cell styling
|
44 |
-
* Image support (gif, png (8, 24 and 32 bit with alpha channel), bmp & jpeg)
|
45 |
-
* No dependencies on external PDF libraries, thanks to the R&OS PDF class
|
46 |
-
* Inline PHP support
|
47 |
-
* Basic SVG support
|
48 |
-
|
49 |
-
## Requirements
|
50 |
-
|
51 |
-
* PHP version 5.4.0 or higher
|
52 |
-
* DOM extension
|
53 |
-
* GD extension
|
54 |
-
* MBString extension
|
55 |
-
* php-font-lib
|
56 |
-
* php-svg-lib
|
57 |
-
|
58 |
-
### Recommendations
|
59 |
-
|
60 |
-
* OPcache (OPcache, XCache, APC, etc.): improves performance
|
61 |
-
* IMagick or GMagick extension: improves image processing performance
|
62 |
-
|
63 |
-
Visit the wiki for more information:
|
64 |
-
https://github.com/dompdf/dompdf/wiki/Requirements
|
65 |
-
|
66 |
-
## About Fonts & Character Encoding
|
67 |
-
|
68 |
-
PDF documents internally support the following fonts: Helvetica, Times-Roman,
|
69 |
-
Courier, Zapf-Dingbats, & Symbol. These fonts only support Windows ANSI
|
70 |
-
encoding. In order for a PDF to display characters that are not available in
|
71 |
-
Windows ANSI you must supply an external font. Dompdf will embed any referenced
|
72 |
-
font in the PDF so long as it has been pre-loaded or is accessible to dompdf and
|
73 |
-
reference in CSS @font-face rules. See the
|
74 |
-
[font overview](https://github.com/dompdf/dompdf/wiki/About-Fonts-and-Character-Encoding)
|
75 |
-
for more information on how to use fonts.
|
76 |
-
|
77 |
-
The [DejaVu TrueType fonts](http://dejavu-fonts.org) have been pre-installed
|
78 |
-
to give dompdf decent Unicode character coverage by default. To use the DejaVu
|
79 |
-
fonts reference the font in your stylesheet, e.g. `body { font-family: DejaVu
|
80 |
-
Sans; }` (for DejaVu Sans). The following DejaVu 2.34 fonts are available:
|
81 |
-
DejaVu Sans, DejaVu Serif, and DejaVu Sans Mono.
|
82 |
-
|
83 |
-
## Easy Installation
|
84 |
-
|
85 |
-
### Install with composer
|
86 |
-
|
87 |
-
To install with [Composer](https://getcomposer.org/), simply require the
|
88 |
-
latest version of this package.
|
89 |
-
|
90 |
-
```bash
|
91 |
-
composer require dompdf/dompdf
|
92 |
-
```
|
93 |
-
|
94 |
-
Make sure that the autoload file from Composer is loaded.
|
95 |
-
|
96 |
-
```php
|
97 |
-
// somewhere early in your project's loading, require the Composer autoloader
|
98 |
-
// see: http://getcomposer.org/doc/00-intro.md
|
99 |
-
require 'vendor/autoload.php';
|
100 |
-
|
101 |
-
```
|
102 |
-
|
103 |
-
### Download and install
|
104 |
-
|
105 |
-
Download an archive of dompdf and extract it into the directory where dompdf
|
106 |
-
will reside
|
107 |
-
* You can download stable copies of dompdf from
|
108 |
-
https://github.com/dompdf/dompdf/releases
|
109 |
-
* Or download a nightly (the latest, unreleased code) from
|
110 |
-
http://eclecticgeek.com/dompdf
|
111 |
-
|
112 |
-
Use the packaged release autoloader to load dompdf, libraries,
|
113 |
-
and helper functions in your PHP:
|
114 |
-
|
115 |
-
```php
|
116 |
-
// include autoloader
|
117 |
-
require_once 'dompdf/autoload.inc.php';
|
118 |
-
```
|
119 |
-
|
120 |
-
### Install with git
|
121 |
-
|
122 |
-
From the command line, switch to the directory where dompdf will reside and run
|
123 |
-
the following commands:
|
124 |
-
|
125 |
-
```sh
|
126 |
-
git clone https://github.com/dompdf/dompdf.git
|
127 |
-
cd dompdf
|
128 |
-
|
129 |
-
git clone https://github.com/PhenX/php-font-lib.git lib/php-font-lib
|
130 |
-
cd lib/php-font-lib
|
131 |
-
git checkout 0.5.1
|
132 |
-
cd ..
|
133 |
-
|
134 |
-
git clone https://github.com/PhenX/php-svg-lib.git php-svg-lib
|
135 |
-
cd php-svg-lib
|
136 |
-
git checkout v0.3
|
137 |
-
```
|
138 |
-
|
139 |
-
Require dompdf, libraries, and helper functions in your PHP:
|
140 |
-
|
141 |
-
```php
|
142 |
-
require_once 'dompdf/lib/html5lib/Parser.php';
|
143 |
-
require_once 'dompdf/lib/php-font-lib/src/FontLib/Autoloader.php';
|
144 |
-
require_once 'dompdf/lib/php-svg-lib/src/autoload.php';
|
145 |
-
require_once 'dompdf/src/Autoloader.php';
|
146 |
-
Dompdf\Autoloader::register();
|
147 |
-
```
|
148 |
-
|
149 |
-
## Quick Start
|
150 |
-
|
151 |
-
Just pass your HTML in to dompdf and stream the output:
|
152 |
-
|
153 |
-
```php
|
154 |
-
// reference the Dompdf namespace
|
155 |
-
use Dompdf\Dompdf;
|
156 |
-
|
157 |
-
// instantiate and use the dompdf class
|
158 |
-
$dompdf = new Dompdf();
|
159 |
-
$dompdf->loadHtml('hello world');
|
160 |
-
|
161 |
-
// (Optional) Setup the paper size and orientation
|
162 |
-
$dompdf->setPaper('A4', 'landscape');
|
163 |
-
|
164 |
-
// Render the HTML as PDF
|
165 |
-
$dompdf->render();
|
166 |
-
|
167 |
-
// Output the generated PDF to Browser
|
168 |
-
$dompdf->stream();
|
169 |
-
```
|
170 |
-
|
171 |
-
### Setting Options
|
172 |
-
|
173 |
-
Set options during dompdf instantiation:
|
174 |
-
|
175 |
-
```php
|
176 |
-
use Dompdf\Dompdf;
|
177 |
-
use Dompdf\Options;
|
178 |
-
|
179 |
-
$options = new Options();
|
180 |
-
$options->set('defaultFont', 'Courier');
|
181 |
-
$dompdf = new Dompdf($options);
|
182 |
-
```
|
183 |
-
|
184 |
-
or at run time
|
185 |
-
|
186 |
-
```php
|
187 |
-
use Dompdf\Dompdf;
|
188 |
-
|
189 |
-
$dompdf = new Dompdf();
|
190 |
-
$dompdf->set_option('defaultFont', 'Courier');
|
191 |
-
```
|
192 |
-
|
193 |
-
See [Dompdf\Options](src/Options.php) for a list of available options.
|
194 |
-
|
195 |
-
|
196 |
-
## Limitations (Known Issues)
|
197 |
-
|
198 |
-
* Dompdf is not particularly tolerant to poorly-formed HTML input. To avoid
|
199 |
-
any unexpected rendering issues you should either enable the built-in HTML5
|
200 |
-
parser at runtime (`$dompdf->set_option('isHtml5ParserEnabled', true);`)
|
201 |
-
or run your HTML through a HTML validator/cleaner (such as
|
202 |
-
[Tidy](http://tidy.sourceforge.net) or the
|
203 |
-
[W3C Markup Validation Service](http://validator.w3.org)).
|
204 |
-
* Large files or large tables can take a while to render.
|
205 |
-
* CSS float is in development and may not produce the desired result
|
206 |
-
|
207 |
-
---
|
208 |
-
|
209 |
-
[![Donate button](https://www.paypal.com/en_US/i/btn/btn_donate_SM.gif)](http://goo.gl/DSvWf)
|
210 |
-
|
211 |
-
*If you find this project useful, please consider making a donation. Any funds donated will be used to help further development on this project.)*
|
1 |
+
Dompdf
|
2 |
+
======
|
3 |
+
|
4 |
+
[![Build Status](https://travis-ci.org/dompdf/dompdf.png?branch=master)](https://travis-ci.org/dompdf/dompdf)
|
5 |
+
[![Latest Stable Version](https://poser.pugx.org/dompdf/dompdf/v/stable.png)](https://packagist.org/packages/dompdf/dompdf)
|
6 |
+
[![Total Downloads](https://poser.pugx.org/dompdf/dompdf/downloads.png)](https://packagist.org/packages/dompdf/dompdf)
|
7 |
+
[![Latest Unstable Version](https://poser.pugx.org/dompdf/dompdf/v/unstable.png)](https://packagist.org/packages/dompdf/dompdf)
|
8 |
+
[![License](https://poser.pugx.org/dompdf/dompdf/license.png)](https://packagist.org/packages/dompdf/dompdf)
|
9 |
+
|
10 |
+
**Dompdf is an HTML to PDF converter**
|
11 |
+
|
12 |
+
At its heart, dompdf is (mostly) a [CSS 2.1](http://www.w3.org/TR/CSS2/) compliant
|
13 |
+
HTML layout and rendering engine written in PHP. It is a style-driven renderer:
|
14 |
+
it will download and read external stylesheets, inline style tags, and the style
|
15 |
+
attributes of individual HTML elements. It also supports most presentational
|
16 |
+
HTML attributes.
|
17 |
+
|
18 |
+
*This document applies to the latest stable code which may not reflect the current
|
19 |
+
release. For released code please
|
20 |
+
[navigate to the appropriate tag](https://github.com/dompdf/dompdf/tags).*
|
21 |
+
|
22 |
+
----
|
23 |
+
|
24 |
+
**Check out the [demo](https://dompdf.net/examples.php) and ask any
|
25 |
+
question on [StackOverflow](http://stackoverflow.com/questions/tagged/dompdf) or
|
26 |
+
on the [Google Groups](http://groups.google.com/group/dompdf).**
|
27 |
+
|
28 |
+
Follow us on [![Twitter](http://twitter-badges.s3.amazonaws.com/twitter-a.png)](http://www.twitter.com/dompdf) or
|
29 |
+
[![Follow us on Google+](https://ssl.gstatic.com/images/icons/gplus-16.png)](https://plus.google.com/108710008521858993320?prsrc=3).
|
30 |
+
|
31 |
+
---
|
32 |
+
|
33 |
+
|
34 |
+
|
35 |
+
## Features
|
36 |
+
|
37 |
+
* Handles most CSS 2.1 and a few CSS3 properties, including @import, @media &
|
38 |
+
@page rules
|
39 |
+
* Supports most presentational HTML 4.0 attributes
|
40 |
+
* Supports external stylesheets, either local or through http/ftp (via
|
41 |
+
fopen-wrappers)
|
42 |
+
* Supports complex tables, including row & column spans, separate & collapsed
|
43 |
+
border models, individual cell styling
|
44 |
+
* Image support (gif, png (8, 24 and 32 bit with alpha channel), bmp & jpeg)
|
45 |
+
* No dependencies on external PDF libraries, thanks to the R&OS PDF class
|
46 |
+
* Inline PHP support
|
47 |
+
* Basic SVG support
|
48 |
+
|
49 |
+
## Requirements
|
50 |
+
|
51 |
+
* PHP version 5.4.0 or higher
|
52 |
+
* DOM extension
|
53 |
+
* GD extension
|
54 |
+
* MBString extension
|
55 |
+
* php-font-lib
|
56 |
+
* php-svg-lib
|
57 |
+
|
58 |
+
### Recommendations
|
59 |
+
|
60 |
+
* OPcache (OPcache, XCache, APC, etc.): improves performance
|
61 |
+
* IMagick or GMagick extension: improves image processing performance
|
62 |
+
|
63 |
+
Visit the wiki for more information:
|
64 |
+
https://github.com/dompdf/dompdf/wiki/Requirements
|
65 |
+
|
66 |
+
## About Fonts & Character Encoding
|
67 |
+
|
68 |
+
PDF documents internally support the following fonts: Helvetica, Times-Roman,
|
69 |
+
Courier, Zapf-Dingbats, & Symbol. These fonts only support Windows ANSI
|
70 |
+
encoding. In order for a PDF to display characters that are not available in
|
71 |
+
Windows ANSI you must supply an external font. Dompdf will embed any referenced
|
72 |
+
font in the PDF so long as it has been pre-loaded or is accessible to dompdf and
|
73 |
+
reference in CSS @font-face rules. See the
|
74 |
+
[font overview](https://github.com/dompdf/dompdf/wiki/About-Fonts-and-Character-Encoding)
|
75 |
+
for more information on how to use fonts.
|
76 |
+
|
77 |
+
The [DejaVu TrueType fonts](http://dejavu-fonts.org) have been pre-installed
|
78 |
+
to give dompdf decent Unicode character coverage by default. To use the DejaVu
|
79 |
+
fonts reference the font in your stylesheet, e.g. `body { font-family: DejaVu
|
80 |
+
Sans; }` (for DejaVu Sans). The following DejaVu 2.34 fonts are available:
|
81 |
+
DejaVu Sans, DejaVu Serif, and DejaVu Sans Mono.
|
82 |
+
|
83 |
+
## Easy Installation
|
84 |
+
|
85 |
+
### Install with composer
|
86 |
+
|
87 |
+
To install with [Composer](https://getcomposer.org/), simply require the
|
88 |
+
latest version of this package.
|
89 |
+
|
90 |
+
```bash
|
91 |
+
composer require dompdf/dompdf
|
92 |
+
```
|
93 |
+
|
94 |
+
Make sure that the autoload file from Composer is loaded.
|
95 |
+
|
96 |
+
```php
|
97 |
+
// somewhere early in your project's loading, require the Composer autoloader
|
98 |
+
// see: http://getcomposer.org/doc/00-intro.md
|
99 |
+
require 'vendor/autoload.php';
|
100 |
+
|
101 |
+
```
|
102 |
+
|
103 |
+
### Download and install
|
104 |
+
|
105 |
+
Download an archive of dompdf and extract it into the directory where dompdf
|
106 |
+
will reside
|
107 |
+
* You can download stable copies of dompdf from
|
108 |
+
https://github.com/dompdf/dompdf/releases
|
109 |
+
* Or download a nightly (the latest, unreleased code) from
|
110 |
+
http://eclecticgeek.com/dompdf
|
111 |
+
|
112 |
+
Use the packaged release autoloader to load dompdf, libraries,
|
113 |
+
and helper functions in your PHP:
|
114 |
+
|
115 |
+
```php
|
116 |
+
// include autoloader
|
117 |
+
require_once 'dompdf/autoload.inc.php';
|
118 |
+
```
|
119 |
+
|
120 |
+
### Install with git
|
121 |
+
|
122 |
+
From the command line, switch to the directory where dompdf will reside and run
|
123 |
+
the following commands:
|
124 |
+
|
125 |
+
```sh
|
126 |
+
git clone https://github.com/dompdf/dompdf.git
|
127 |
+
cd dompdf
|
128 |
+
|
129 |
+
git clone https://github.com/PhenX/php-font-lib.git lib/php-font-lib
|
130 |
+
cd lib/php-font-lib
|
131 |
+
git checkout 0.5.1
|
132 |
+
cd ..
|
133 |
+
|
134 |
+
git clone https://github.com/PhenX/php-svg-lib.git php-svg-lib
|
135 |
+
cd php-svg-lib
|
136 |
+
git checkout v0.3
|
137 |
+
```
|
138 |
+
|
139 |
+
Require dompdf, libraries, and helper functions in your PHP:
|
140 |
+
|
141 |
+
```php
|
142 |
+
require_once 'dompdf/lib/html5lib/Parser.php';
|
143 |
+
require_once 'dompdf/lib/php-font-lib/src/FontLib/Autoloader.php';
|
144 |
+
require_once 'dompdf/lib/php-svg-lib/src/autoload.php';
|
145 |
+
require_once 'dompdf/src/Autoloader.php';
|
146 |
+
Dompdf\Autoloader::register();
|
147 |
+
```
|
148 |
+
|
149 |
+
## Quick Start
|
150 |
+
|
151 |
+
Just pass your HTML in to dompdf and stream the output:
|
152 |
+
|
153 |
+
```php
|
154 |
+
// reference the Dompdf namespace
|
155 |
+
use Dompdf\Dompdf;
|
156 |
+
|
157 |
+
// instantiate and use the dompdf class
|
158 |
+
$dompdf = new Dompdf();
|
159 |
+
$dompdf->loadHtml('hello world');
|
160 |
+
|
161 |
+
// (Optional) Setup the paper size and orientation
|
162 |
+
$dompdf->setPaper('A4', 'landscape');
|
163 |
+
|
164 |
+
// Render the HTML as PDF
|
165 |
+
$dompdf->render();
|
166 |
+
|
167 |
+
// Output the generated PDF to Browser
|
168 |
+
$dompdf->stream();
|
169 |
+
```
|
170 |
+
|
171 |
+
### Setting Options
|
172 |
+
|
173 |
+
Set options during dompdf instantiation:
|
174 |
+
|
175 |
+
```php
|
176 |
+
use Dompdf\Dompdf;
|
177 |
+
use Dompdf\Options;
|
178 |
+
|
179 |
+
$options = new Options();
|
180 |
+
$options->set('defaultFont', 'Courier');
|
181 |
+
$dompdf = new Dompdf($options);
|
182 |
+
```
|
183 |
+
|
184 |
+
or at run time
|
185 |
+
|
186 |
+
```php
|
187 |
+
use Dompdf\Dompdf;
|
188 |
+
|
189 |
+
$dompdf = new Dompdf();
|
190 |
+
$dompdf->set_option('defaultFont', 'Courier');
|
191 |
+
```
|
192 |
+
|
193 |
+
See [Dompdf\Options](src/Options.php) for a list of available options.
|
194 |
+
|
195 |
+
|
196 |
+
## Limitations (Known Issues)
|
197 |
+
|
198 |
+
* Dompdf is not particularly tolerant to poorly-formed HTML input. To avoid
|
199 |
+
any unexpected rendering issues you should either enable the built-in HTML5
|
200 |
+
parser at runtime (`$dompdf->set_option('isHtml5ParserEnabled', true);`)
|
201 |
+
or run your HTML through a HTML validator/cleaner (such as
|
202 |
+
[Tidy](http://tidy.sourceforge.net) or the
|
203 |
+
[W3C Markup Validation Service](http://validator.w3.org)).
|
204 |
+
* Large files or large tables can take a while to render.
|
205 |
+
* CSS float is in development and may not produce the desired result
|
206 |
+
|
207 |
+
---
|
208 |
+
|
209 |
+
[![Donate button](https://www.paypal.com/en_US/i/btn/btn_donate_SM.gif)](http://goo.gl/DSvWf)
|
210 |
+
|
211 |
+
*If you find this project useful, please consider making a donation. Any funds donated will be used to help further development on this project.)*
|
vendor/dompdf/dompdf/composer.json
CHANGED
@@ -1,44 +1,44 @@
|
|
1 |
-
{
|
2 |
-
"name": "dompdf/dompdf",
|
3 |
-
"type": "library",
|
4 |
-
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
|
5 |
-
"homepage": "https://github.com/dompdf/dompdf",
|
6 |
-
"license": "LGPL-2.1",
|
7 |
-
"authors": [
|
8 |
-
{
|
9 |
-
"name": "Fabien Ménager",
|
10 |
-
"email": "fabien.menager@gmail.com"
|
11 |
-
},
|
12 |
-
{
|
13 |
-
"name": "Brian Sweeney",
|
14 |
-
"email": "eclecticgeek@gmail.com"
|
15 |
-
},
|
16 |
-
{
|
17 |
-
"name": "Gabriel Bull",
|
18 |
-
"email": "me@gabrielbull.com"
|
19 |
-
}
|
20 |
-
],
|
21 |
-
"autoload": {
|
22 |
-
"psr-4" : {
|
23 |
-
"Dompdf\\" : "src/"
|
24 |
-
},
|
25 |
-
"classmap" : ["lib/"]
|
26 |
-
},
|
27 |
-
"require": {
|
28 |
-
"php": ">=5.4.0",
|
29 |
-
"ext-gd": "*",
|
30 |
-
"ext-dom": "*",
|
31 |
-
"ext-mbstring": "*",
|
32 |
-
"phenx/php-font-lib": "0.5.*",
|
33 |
-
"phenx/php-svg-lib": "0.3.*"
|
34 |
-
},
|
35 |
-
"require-dev": {
|
36 |
-
"phpunit/phpunit": "4.8.*",
|
37 |
-
"squizlabs/php_codesniffer": "2.*"
|
38 |
-
},
|
39 |
-
"extra": {
|
40 |
-
"branch-alias": {
|
41 |
-
"dev-develop": "0.7-dev"
|
42 |
-
}
|
43 |
-
}
|
44 |
-
}
|
1 |
+
{
|
2 |
+
"name": "dompdf/dompdf",
|
3 |
+
"type": "library",
|
4 |
+
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
|
5 |
+
"homepage": "https://github.com/dompdf/dompdf",
|
6 |
+
"license": "LGPL-2.1",
|
7 |
+
"authors": [
|
8 |
+
{
|
9 |
+
"name": "Fabien Ménager",
|
10 |
+
"email": "fabien.menager@gmail.com"
|
11 |
+
},
|
12 |
+
{
|
13 |
+
"name": "Brian Sweeney",
|
14 |
+
"email": "eclecticgeek@gmail.com"
|
15 |
+
},
|
16 |
+
{
|
17 |
+
"name": "Gabriel Bull",
|
18 |
+
"email": "me@gabrielbull.com"
|
19 |
+
}
|
20 |
+
],
|
21 |
+
"autoload": {
|
22 |
+
"psr-4" : {
|
23 |
+
"Dompdf\\" : "src/"
|
24 |
+
},
|
25 |
+
"classmap" : ["lib/"]
|
26 |
+
},
|
27 |
+
"require": {
|
28 |
+
"php": ">=5.4.0",
|
29 |
+
"ext-gd": "*",
|
30 |
+
"ext-dom": "*",
|
31 |
+
"ext-mbstring": "*",
|
32 |
+
"phenx/php-font-lib": "0.5.*",
|
33 |
+
"phenx/php-svg-lib": "0.3.*"
|
34 |
+
},
|
35 |
+
"require-dev": {
|
36 |
+
"phpunit/phpunit": "4.8.*",
|
37 |
+
"squizlabs/php_codesniffer": "2.*"
|
38 |
+
},
|
39 |
+
"extra": {
|
40 |
+
"branch-alias": {
|
41 |
+
"dev-develop": "0.7-dev"
|
42 |
+
}
|
43 |
+
}
|
44 |
+
}
|
vendor/dompdf/dompdf/lib/Cpdf.php
CHANGED
@@ -1,5640 +1,5640 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* A PHP class to provide the basic functionality to create a pdf document without
|
4 |
-
* any requirement for additional modules.
|
5 |
-
*
|
6 |
-
* Extended by Orion Richardson to support Unicode / UTF-8 characters using
|
7 |
-
* TCPDF and others as a guide.
|
8 |
-
*
|
9 |
-
* @author Wayne Munro <pdf@ros.co.nz>
|
10 |
-
* @author Orion Richardson <orionr@yahoo.com>
|
11 |
-
* @author Helmut Tischer <htischer@weihenstephan.org>
|
12 |
-
* @author Ryan H. Masten <ryan.masten@gmail.com>
|
13 |
-
* @author Brian Sweeney <eclecticgeek@gmail.com>
|
14 |
-
* @author Fabien Ménager <fabien.menager@gmail.com>
|
15 |
-
* @license Public Domain http://creativecommons.org/licenses/publicdomain/
|
16 |
-
* @package Cpdf
|
17 |
-
*/
|
18 |
-
use FontLib\Font;
|
19 |
-
use FontLib\BinaryStream;
|
20 |
-
|
21 |
-
class Cpdf
|
22 |
-
{
|
23 |
-
|
24 |
-
/**
|
25 |
-
* @var integer The current number of pdf objects in the document
|
26 |
-
*/
|
27 |
-
public $numObj = 0;
|
28 |
-
|
29 |
-
/**
|
30 |
-
* @var array This array contains all of the pdf objects, ready for final assembly
|
31 |
-
*/
|
32 |
-
public $objects = array();
|
33 |
-
|
34 |
-
/**
|
35 |
-
* @var integer The objectId (number within the objects array) of the document catalog
|
36 |
-
*/
|
37 |
-
public $catalogId;
|
38 |
-
|
39 |
-
/**
|
40 |
-
* @var array Array carrying information about the fonts that the system currently knows about
|
41 |
-
* Used to ensure that a font is not loaded twice, among other things
|
42 |
-
*/
|
43 |
-
public $fonts = array();
|
44 |
-
|
45 |
-
/**
|
46 |
-
* @var string The default font metrics file to use if no other font has been loaded.
|
47 |
-
* The path to the directory containing the font metrics should be included
|
48 |
-
*/
|
49 |
-
public $defaultFont = './fonts/Helvetica.afm';
|
50 |
-
|
51 |
-
/**
|
52 |
-
* @string A record of the current font
|
53 |
-
*/
|
54 |
-
public $currentFont = '';
|
55 |
-
|
56 |
-
/**
|
57 |
-
* @var string The current base font
|
58 |
-
*/
|
59 |
-
public $currentBaseFont = '';
|
60 |
-
|
61 |
-
/**
|
62 |
-
* @var integer The number of the current font within the font array
|
63 |
-
*/
|
64 |
-
public $currentFontNum = 0;
|
65 |
-
|
66 |
-
/**
|
67 |
-
* @var integer
|
68 |
-
*/
|
69 |
-
public $currentNode;
|
70 |
-
|
71 |
-
/**
|
72 |
-
* @var integer Object number of the current page
|
73 |
-
*/
|
74 |
-
public $currentPage;
|
75 |
-
|
76 |
-
/**
|
77 |
-
* @var integer Object number of the currently active contents block
|
78 |
-
*/
|
79 |
-
public $currentContents;
|
80 |
-
|
81 |
-
/**
|
82 |
-
* @var integer Number of fonts within the system
|
83 |
-
*/
|
84 |
-
public $numFonts = 0;
|
85 |
-
|
86 |
-
/**
|
87 |
-
* @var integer Number of graphic state resources used
|
88 |
-
*/
|
89 |
-
private $numStates = 0;
|
90 |
-
|
91 |
-
/**
|
92 |
-
* @var array Number of graphic state resources used
|
93 |
-
*/
|
94 |
-
private $gstates = array();
|
95 |
-
|
96 |
-
/**
|
97 |
-
* @var array Current color for fill operations, defaults to inactive value,
|
98 |
-
* all three components should be between 0 and 1 inclusive when active
|
99 |
-
*/
|
100 |
-
public $currentColor = null;
|
101 |
-
|
102 |
-
/**
|
103 |
-
* @var array Current color for stroke operations (lines etc.)
|
104 |
-
*/
|
105 |
-
public $currentStrokeColor = null;
|
106 |
-
|
107 |
-
/**
|
108 |
-
* @var string Fill rule (nonzero or evenodd)
|
109 |
-
*/
|
110 |
-
public $fillRule = "nonzero";
|
111 |
-
|
112 |
-
/**
|
113 |
-
* @var string Current style that lines are drawn in
|
114 |
-
*/
|
115 |
-
public $currentLineStyle = '';
|
116 |
-
|
117 |
-
/**
|
118 |
-
* @var array Current line transparency (partial graphics state)
|
119 |
-
*/
|
120 |
-
public $currentLineTransparency = array("mode" => "Normal", "opacity" => 1.0);
|
121 |
-
|
122 |
-
/**
|
123 |
-
* array Current fill transparency (partial graphics state)
|
124 |
-
*/
|
125 |
-
public $currentFillTransparency = array("mode" => "Normal", "opacity" => 1.0);
|
126 |
-
|
127 |
-
/**
|
128 |
-
* @var array An array which is used to save the state of the document, mainly the colors and styles
|
129 |
-
* it is used to temporarily change to another state, then change back to what it was before
|
130 |
-
*/
|
131 |
-
public $stateStack = array();
|
132 |
-
|
133 |
-
/**
|
134 |
-
* @var integer Number of elements within the state stack
|
135 |
-
*/
|
136 |
-
public $nStateStack = 0;
|
137 |
-
|
138 |
-
/**
|
139 |
-
* @var integer Number of page objects within the document
|
140 |
-
*/
|
141 |
-
public $numPages = 0;
|
142 |
-
|
143 |
-
/**
|
144 |
-
* @var array Object Id storage stack
|
145 |
-
*/
|
146 |
-
public $stack = array();
|
147 |
-
|
148 |
-
/**
|
149 |
-
* @var integer Number of elements within the object Id storage stack
|
150 |
-
*/
|
151 |
-
public $nStack = 0;
|
152 |
-
|
153 |
-
/**
|
154 |
-
* an array which contains information about the objects which are not firmly attached to pages
|
155 |
-
* these have been added with the addObject function
|
156 |
-
*/
|
157 |
-
public $looseObjects = array();
|
158 |
-
|
159 |
-
/**
|
160 |
-
* array contains information about how the loose objects are to be added to the document
|
161 |
-
*/
|
162 |
-
public $addLooseObjects = array();
|
163 |
-
|
164 |
-
/**
|
165 |
-
* @var integer The objectId of the information object for the document
|
166 |
-
* this contains authorship, title etc.
|
167 |
-
*/
|
168 |
-
public $infoObject = 0;
|
169 |
-
|
170 |
-
/**
|
171 |
-
* @var integer Number of images being tracked within the document
|
172 |
-
*/
|
173 |
-
public $numImages = 0;
|
174 |
-
|
175 |
-
/**
|
176 |
-
* @var array An array containing options about the document
|
177 |
-
* it defaults to turning on the compression of the objects
|
178 |
-
*/
|
179 |
-
public $options = array('compression' => true);
|
180 |
-
|
181 |
-
/**
|
182 |
-
* @var integer The objectId of the first page of the document
|
183 |
-
*/
|
184 |
-
public $firstPageId;
|
185 |
-
|
186 |
-
/**
|
187 |
-
* @var integer The object Id of the procset object
|
188 |
-
*/
|
189 |
-
public $procsetObjectId;
|
190 |
-
|
191 |
-
/**
|
192 |
-
* @var array Store the information about the relationship between font families
|
193 |
-
* this used so that the code knows which font is the bold version of another font, etc.
|
194 |
-
* the value of this array is initialised in the constructor function.
|
195 |
-
*/
|
196 |
-
public $fontFamilies = array();
|
197 |
-
|
198 |
-
/**
|
199 |
-
* @var string Folder for php serialized formats of font metrics files.
|
200 |
-
* If empty string, use same folder as original metrics files.
|
201 |
-
* This can be passed in from class creator.
|
202 |
-
* If this folder does not exist or is not writable, Cpdf will be **much** slower.
|
203 |
-
* Because of potential trouble with php safe mode, folder cannot be created at runtime.
|
204 |
-
*/
|
205 |
-
public $fontcache = '';
|
206 |
-
|
207 |
-
/**
|
208 |
-
* @var integer The version of the font metrics cache file.
|
209 |
-
* This value must be manually incremented whenever the internal font data structure is modified.
|
210 |
-
*/
|
211 |
-
public $fontcacheVersion = 6;
|
212 |
-
|
213 |
-
/**
|
214 |
-
* @var string Temporary folder.
|
215 |
-
* If empty string, will attempt system tmp folder.
|
216 |
-
* This can be passed in from class creator.
|
217 |
-
*/
|
218 |
-
public $tmp = '';
|
219 |
-
|
220 |
-
/**
|
221 |
-
* @var string Track if the current font is bolded or italicised
|
222 |
-
*/
|
223 |
-
public $currentTextState = '';
|
224 |
-
|
225 |
-
/**
|
226 |
-
* @var string Messages are stored here during processing, these can be selected afterwards to give some useful debug information
|
227 |
-
*/
|
228 |
-
public $messages = '';
|
229 |
-
|
230 |
-
/**
|
231 |
-
* @var string The encryption array for the document encryption is stored here
|
232 |
-
*/
|
233 |
-
public $arc4 = '';
|
234 |
-
|
235 |
-
/**
|
236 |
-
* @var integer The object Id of the encryption information
|
237 |
-
*/
|
238 |
-
public $arc4_objnum = 0;
|
239 |
-
|
240 |
-
/**
|
241 |
-
* @var string The file identifier, used to uniquely identify a pdf document
|
242 |
-
*/
|
243 |
-
public $fileIdentifier = '';
|
244 |
-
|
245 |
-
/**
|
246 |
-
* @var boolean A flag to say if a document is to be encrypted or not
|
247 |
-
*/
|
248 |
-
public $encrypted = false;
|
249 |
-
|
250 |
-
/**
|
251 |
-
* @var string The encryption key for the encryption of all the document content (structure is not encrypted)
|
252 |
-
*/
|
253 |
-
public $encryptionKey = '';
|
254 |
-
|
255 |
-
/**
|
256 |
-
* @var array Array which forms a stack to keep track of nested callback functions
|
257 |
-
*/
|
258 |
-
public $callback = array();
|
259 |
-
|
260 |
-
/**
|
261 |
-
* @var integer The number of callback functions in the callback array
|
262 |
-
*/
|
263 |
-
public $nCallback = 0;
|
264 |
-
|
265 |
-
/**
|
266 |
-
* @var array Store label->id pairs for named destinations, these will be used to replace internal links
|
267 |
-
* done this way so that destinations can be defined after the location that links to them
|
268 |
-
*/
|
269 |
-
public $destinations = array();
|
270 |
-
|
271 |
-
/**
|
272 |
-
* @var array Store the stack for the transaction commands, each item in here is a record of the values of all the
|
273 |
-
* publiciables within the class, so that the user can rollback at will (from each 'start' command)
|
274 |
-
* note that this includes the objects array, so these can be large.
|
275 |
-
*/
|
276 |
-
public $checkpoint = '';
|
277 |
-
|
278 |
-
/**
|
279 |
-
* @var array Table of Image origin filenames and image labels which were already added with o_image().
|
280 |
-
* Allows to merge identical images
|
281 |
-
*/
|
282 |
-
public $imagelist = array();
|
283 |
-
|
284 |
-
/**
|
285 |
-
* @var boolean Whether the text passed in should be treated as Unicode or just local character set.
|
286 |
-
*/
|
287 |
-
public $isUnicode = false;
|
288 |
-
|
289 |
-
/**
|
290 |
-
* @var string the JavaScript code of the document
|
291 |
-
*/
|
292 |
-
public $javascript = '';
|
293 |
-
|
294 |
-
/**
|
295 |
-
* @var boolean whether the compression is possible
|
296 |
-
*/
|
297 |
-
protected $compressionReady = false;
|
298 |
-
|
299 |
-
/**
|
300 |
-
* @var array Current page size
|
301 |
-
*/
|
302 |
-
protected $currentPageSize = array("width" => 0, "height" => 0);
|
303 |
-
|
304 |
-
/**
|
305 |
-
* @var array All the chars that will be required in the font subsets
|
306 |
-
*/
|
307 |
-
protected $stringSubsets = array();
|
308 |
-
|
309 |
-
/**
|
310 |
-
* @var string The target internal encoding
|
311 |
-
*/
|
312 |
-
static protected $targetEncoding = 'Windows-1252';
|
313 |
-
|
314 |
-
/**
|
315 |
-
* @var array The list of the core fonts
|
316 |
-
*/
|
317 |
-
static protected $coreFonts = array(
|
318 |
-
'courier',
|
319 |
-
'courier-bold',
|
320 |
-
'courier-oblique',
|
321 |
-
'courier-boldoblique',
|
322 |
-
'helvetica',
|
323 |
-
'helvetica-bold',
|
324 |
-
'helvetica-oblique',
|
325 |
-
'helvetica-boldoblique',
|
326 |
-
'times-roman',
|
327 |
-
'times-bold',
|
328 |
-
'times-italic',
|
329 |
-
'times-bolditalic',
|
330 |
-
'symbol',
|
331 |
-
'zapfdingbats'
|
332 |
-
);
|
333 |
-
|
334 |
-
/**
|
335 |
-
* Class constructor
|
336 |
-
* This will start a new document
|
337 |
-
*
|
338 |
-
* @param array $pageSize Array of 4 numbers, defining the bottom left and upper right corner of the page. first two are normally zero.
|
339 |
-
* @param boolean $isUnicode Whether text will be treated as Unicode or not.
|
340 |
-
* @param string $fontcache The font cache folder
|
341 |
-
* @param string $tmp The temporary folder
|
342 |
-
*/
|
343 |
-
function __construct($pageSize = array(0, 0, 612, 792), $isUnicode = false, $fontcache = '', $tmp = '')
|
344 |
-
{
|
345 |
-
$this->isUnicode = $isUnicode;
|
346 |
-
$this->fontcache = rtrim($fontcache, DIRECTORY_SEPARATOR."/\\");
|
347 |
-
$this->tmp = ($tmp !== '' ? $tmp : sys_get_temp_dir());
|
348 |
-
$this->newDocument($pageSize);
|
349 |
-
|
350 |
-
$this->compressionReady = function_exists('gzcompress');
|
351 |
-
|
352 |
-
if (in_array('Windows-1252', mb_list_encodings())) {
|
353 |
-
self::$targetEncoding = 'Windows-1252';
|
354 |
-
}
|
355 |
-
|
356 |
-
// also initialize the font families that are known about already
|
357 |
-
$this->setFontFamily('init');
|
358 |
-
}
|
359 |
-
|
360 |
-
/**
|
361 |
-
* Document object methods (internal use only)
|
362 |
-
*
|
363 |
-
* There is about one object method for each type of object in the pdf document
|
364 |
-
* Each function has the same call list ($id,$action,$options).
|
365 |
-
* $id = the object ID of the object, or what it is to be if it is being created
|
366 |
-
* $action = a string specifying the action to be performed, though ALL must support:
|
367 |
-
* 'new' - create the object with the id $id
|
368 |
-
* 'out' - produce the output for the pdf object
|
369 |
-
* $options = optional, a string or array containing the various parameters for the object
|
370 |
-
*
|
371 |
-
* These, in conjunction with the output function are the ONLY way for output to be produced
|
372 |
-
* within the pdf 'file'.
|
373 |
-
*/
|
374 |
-
|
375 |
-
/**
|
376 |
-
* Destination object, used to specify the location for the user to jump to, presently on opening
|
377 |
-
*
|
378 |
-
* @param $id
|
379 |
-
* @param $action
|
380 |
-
* @param string $options
|
381 |
-
* @return string|null
|
382 |
-
*/
|
383 |
-
protected function o_destination($id, $action, $options = '')
|
384 |
-
{
|
385 |
-
switch ($action) {
|
386 |
-
case 'new':
|
387 |
-
$this->objects[$id] = array('t' => 'destination', 'info' => array());
|
388 |
-
$tmp = '';
|
389 |
-
switch ($options['type']) {
|
390 |
-
case 'XYZ':
|
391 |
-
/** @noinspection PhpMissingBreakStatementInspection */
|
392 |
-
case 'FitR':
|
393 |
-
$tmp = ' ' . $options['p3'] . $tmp;
|
394 |
-
case 'FitH':
|
395 |
-
case 'FitV':
|
396 |
-
case 'FitBH':
|
397 |
-
/** @noinspection PhpMissingBreakStatementInspection */
|
398 |
-
case 'FitBV':
|
399 |
-
$tmp = ' ' . $options['p1'] . ' ' . $options['p2'] . $tmp;
|
400 |
-
case 'Fit':
|
401 |
-
case 'FitB':
|
402 |
-
$tmp = $options['type'] . $tmp;
|
403 |
-
$this->objects[$id]['info']['string'] = $tmp;
|
404 |
-
$this->objects[$id]['info']['page'] = $options['page'];
|
405 |
-
}
|
406 |
-
break;
|
407 |
-
|
408 |
-
case 'out':
|
409 |
-
$o = &$this->objects[$id];
|
410 |
-
|
411 |
-
$tmp = $o['info'];
|
412 |
-
$res = "\n$id 0 obj\n" . '[' . $tmp['page'] . ' 0 R /' . $tmp['string'] . "]\nendobj";
|
413 |
-
|
414 |
-
return $res;
|
415 |
-
}
|
416 |
-
|
417 |
-
return null;
|
418 |
-
}
|
419 |
-
|
420 |
-
/**
|
421 |
-
* set the viewer preferences
|
422 |
-
*
|
423 |
-
* @param $id
|
424 |
-
* @param $action
|
425 |
-
* @param string|array $options
|
426 |
-
* @return string|null
|
427 |
-
*/
|
428 |
-
protected function o_viewerPreferences($id, $action, $options = '')
|
429 |
-
{
|
430 |
-
switch ($action) {
|
431 |
-
case 'new':
|
432 |
-
$this->objects[$id] = array('t' => 'viewerPreferences', 'info' => array());
|
433 |
-
break;
|
434 |
-
|
435 |
-
case 'add':
|
436 |
-
$o = &$this->objects[$id];
|
437 |
-
|
438 |
-
foreach ($options as $k => $v) {
|
439 |
-
switch ($k) {
|
440 |
-
// Boolean keys
|
441 |
-
case 'HideToolbar':
|
442 |
-
case 'HideMenubar':
|
443 |
-
case 'HideWindowUI':
|
444 |
-
case 'FitWindow':
|
445 |
-
case 'CenterWindow':
|
446 |
-
case 'DisplayDocTitle':
|
447 |
-
case 'PickTrayByPDFSize':
|
448 |
-
$o['info'][$k] = (bool)$v;
|
449 |
-
break;
|
450 |
-
|
451 |
-
// Integer keys
|
452 |
-
case 'NumCopies':
|
453 |
-
$o['info'][$k] = (int)$v;
|
454 |
-
break;
|
455 |
-
|
456 |
-
// Name keys
|
457 |
-
case 'ViewArea':
|
458 |
-
case 'ViewClip':
|
459 |
-
case 'PrintClip':
|
460 |
-
case 'PrintArea':
|
461 |
-
$o['info'][$k] = (string)$v;
|
462 |
-
break;
|
463 |
-
|
464 |
-
// Named with limited valid values
|
465 |
-
case 'NonFullScreenPageMode':
|
466 |
-
if (!in_array($v, array('UseNone', 'UseOutlines', 'UseThumbs', 'UseOC'))) {
|
467 |
-
continue;
|
468 |
-
}
|
469 |
-
$o['info'][$k] = $v;
|
470 |
-
break;
|
471 |
-
|
472 |
-
case 'Direction':
|
473 |
-
if (!in_array($v, array('L2R', 'R2L'))) {
|
474 |
-
continue;
|
475 |
-
}
|
476 |
-
$o['info'][$k] = $v;
|
477 |
-
break;
|
478 |
-
|
479 |
-
case 'PrintScaling':
|
480 |
-
if (!in_array($v, array('None', 'AppDefault'))) {
|
481 |
-
continue;
|
482 |
-
}
|
483 |
-
$o['info'][$k] = $v;
|
484 |
-
break;
|
485 |
-
|
486 |
-
case 'Duplex':
|
487 |
-
if (!in_array($v, array('None', 'AppDefault'))) {
|
488 |
-
continue;
|
489 |
-
}
|
490 |
-
$o['info'][$k] = $v;
|
491 |
-
break;
|
492 |
-
|
493 |
-
// Integer array
|
494 |
-
case 'PrintPageRange':
|
495 |
-
// Cast to integer array
|
496 |
-
foreach ($v as $vK => $vV) {
|
497 |
-
$v[$vK] = (int)$vV;
|
498 |
-
}
|
499 |
-
$o['info'][$k] = array_values($v);
|
500 |
-
break;
|
501 |
-
}
|
502 |
-
}
|
503 |
-
break;
|
504 |
-
|
505 |
-
case 'out':
|
506 |
-
$o = &$this->objects[$id];
|
507 |
-
$res = "\n$id 0 obj\n<< ";
|
508 |
-
|
509 |
-
foreach ($o['info'] as $k => $v) {
|
510 |
-
if (is_string($v)) {
|
511 |
-
$v = '/' . $v;
|
512 |
-
} elseif (is_int($v)) {
|
513 |
-
$v = (string) $v;
|
514 |
-
} elseif (is_bool($v)) {
|
515 |
-
$v = ($v ? 'true' : 'false');
|
516 |
-
} elseif (is_array($v)) {
|
517 |
-
$v = '[' . implode(' ', $v) . ']';
|
518 |
-
}
|
519 |
-
$res .= "\n/$k $v";
|
520 |
-
}
|
521 |
-
$res .= "\n>>\n";
|
522 |
-
|
523 |
-
return $res;
|
524 |
-
}
|
525 |
-
|
526 |
-
return null;
|
527 |
-
}
|
528 |
-
|
529 |
-
/**
|
530 |
-
* define the document catalog, the overall controller for the document
|
531 |
-
*
|
532 |
-
* @param $id
|
533 |
-
* @param $action
|
534 |
-
* @param string|array $options
|
535 |
-
* @return string|null
|
536 |
-
*/
|
537 |
-
protected function o_catalog($id, $action, $options = '')
|
538 |
-
{
|
539 |
-
if ($action !== 'new') {
|
540 |
-
$o = &$this->objects[$id];
|
541 |
-
}
|
542 |
-
|
543 |
-
switch ($action) {
|
544 |
-
case 'new':
|
545 |
-
$this->objects[$id] = array('t' => 'catalog', 'info' => array());
|
546 |
-
$this->catalogId = $id;
|
547 |
-
break;
|
548 |
-
|
549 |
-
case 'outlines':
|
550 |
-
case 'pages':
|
551 |
-
case 'openHere':
|
552 |
-
case 'javascript':
|
553 |
-
$o['info'][$action] = $options;
|
554 |
-
break;
|
555 |
-
|
556 |
-
case 'viewerPreferences':
|
557 |
-
if (!isset($o['info']['viewerPreferences'])) {
|
558 |
-
$this->numObj++;
|
559 |
-
$this->o_viewerPreferences($this->numObj, 'new');
|
560 |
-
$o['info']['viewerPreferences'] = $this->numObj;
|
561 |
-
}
|
562 |
-
|
563 |
-
$vp = $o['info']['viewerPreferences'];
|
564 |
-
$this->o_viewerPreferences($vp, 'add', $options);
|
565 |
-
|
566 |
-
break;
|
567 |
-
|
568 |
-
case 'out':
|
569 |
-
$res = "\n$id 0 obj\n<< /Type /Catalog";
|
570 |
-
|
571 |
-
foreach ($o['info'] as $k => $v) {
|
572 |
-
switch ($k) {
|
573 |
-
case 'outlines':
|
574 |
-
$res .= "\n/Outlines $v 0 R";
|
575 |
-
break;
|
576 |
-
|
577 |
-
case 'pages':
|
578 |
-
$res .= "\n/Pages $v 0 R";
|
579 |
-
break;
|
580 |
-
|
581 |
-
case 'viewerPreferences':
|
582 |
-
$res .= "\n/ViewerPreferences $v 0 R";
|
583 |
-
break;
|
584 |
-
|
585 |
-
case 'openHere':
|
586 |
-
$res .= "\n/OpenAction $v 0 R";
|
587 |
-
break;
|
588 |
-
|
589 |
-
case 'javascript':
|
590 |
-
$res .= "\n/Names <</JavaScript $v 0 R>>";
|
591 |
-
break;
|
592 |
-
}
|
593 |
-
}
|
594 |
-
|
595 |
-
$res .= " >>\nendobj";
|
596 |
-
|
597 |
-
return $res;
|
598 |
-
}
|
599 |
-
|
600 |
-
return null;
|
601 |
-
}
|
602 |
-
|
603 |
-
/**
|
604 |
-
* object which is a parent to the pages in the document
|
605 |
-
*
|
606 |
-
* @param $id
|
607 |
-
* @param $action
|
608 |
-
* @param string $options
|
609 |
-
* @return string|null
|
610 |
-
*/
|
611 |
-
protected function o_pages($id, $action, $options = '')
|
612 |
-
{
|
613 |
-
if ($action !== 'new') {
|
614 |
-
$o = &$this->objects[$id];
|
615 |
-
}
|
616 |
-
|
617 |
-
switch ($action) {
|
618 |
-
case 'new':
|
619 |
-
$this->objects[$id] = array('t' => 'pages', 'info' => array());
|
620 |
-
$this->o_catalog($this->catalogId, 'pages', $id);
|
621 |
-
break;
|
622 |
-
|
623 |
-
case 'page':
|
624 |
-
if (!is_array($options)) {
|
625 |
-
// then it will just be the id of the new page
|
626 |
-
$o['info']['pages'][] = $options;
|
627 |
-
} else {
|
628 |
-
// then it should be an array having 'id','rid','pos', where rid=the page to which this one will be placed relative
|
629 |
-
// and pos is either 'before' or 'after', saying where this page will fit.
|
630 |
-
if (isset($options['id']) && isset($options['rid']) && isset($options['pos'])) {
|
631 |
-
$i = array_search($options['rid'], $o['info']['pages']);
|
632 |
-
if (isset($o['info']['pages'][$i]) && $o['info']['pages'][$i] == $options['rid']) {
|
633 |
-
|
634 |
-
// then there is a match
|
635 |
-
// make a space
|
636 |
-
switch ($options['pos']) {
|
637 |
-
case 'before':
|
638 |
-
$k = $i;
|
639 |
-
break;
|
640 |
-
|
641 |
-
case 'after':
|
642 |
-
$k = $i + 1;
|
643 |
-
break;
|
644 |
-
|
645 |
-
default:
|
646 |
-
$k = -1;
|
647 |
-
break;
|
648 |
-
}
|
649 |
-
|
650 |
-
if ($k >= 0) {
|
651 |
-
for ($j = count($o['info']['pages']) - 1; $j >= $k; $j--) {
|
652 |
-
$o['info']['pages'][$j + 1] = $o['info']['pages'][$j];
|
653 |
-
}
|
654 |
-
|
655 |
-
$o['info']['pages'][$k] = $options['id'];
|
656 |
-
}
|
657 |
-
}
|
658 |
-
}
|
659 |
-
}
|
660 |
-
break;
|
661 |
-
|
662 |
-
case 'procset':
|
663 |
-
$o['info']['procset'] = $options;
|
664 |
-
break;
|
665 |
-
|
666 |
-
case 'mediaBox':
|
667 |
-
$o['info']['mediaBox'] = $options;
|
668 |
-
// which should be an array of 4 numbers
|
669 |
-
$this->currentPageSize = array('width' => $options[2], 'height' => $options[3]);
|
670 |
-
break;
|
671 |
-
|
672 |
-
case 'font':
|
673 |
-
$o['info']['fonts'][] = array('objNum' => $options['objNum'], 'fontNum' => $options['fontNum']);
|
674 |
-
break;
|
675 |
-
|
676 |
-
case 'extGState':
|
677 |
-
$o['info']['extGStates'][] = array('objNum' => $options['objNum'], 'stateNum' => $options['stateNum']);
|
678 |
-
break;
|
679 |
-
|
680 |
-
case 'xObject':
|
681 |
-
$o['info']['xObjects'][] = array('objNum' => $options['objNum'], 'label' => $options['label']);
|
682 |
-
break;
|
683 |
-
|
684 |
-
case 'out':
|
685 |
-
if (count($o['info']['pages'])) {
|
686 |
-
$res = "\n$id 0 obj\n<< /Type /Pages\n/Kids [";
|
687 |
-
foreach ($o['info']['pages'] as $v) {
|
688 |
-
$res .= "$v 0 R\n";
|
689 |
-
}
|
690 |
-
|
691 |
-
$res .= "]\n/Count " . count($this->objects[$id]['info']['pages']);
|
692 |
-
|
693 |
-
if ((isset($o['info']['fonts']) && count($o['info']['fonts'])) ||
|
694 |
-
isset($o['info']['procset']) ||
|
695 |
-
(isset($o['info']['extGStates']) && count($o['info']['extGStates']))
|
696 |
-
) {
|
697 |
-
$res .= "\n/Resources <<";
|
698 |
-
|
699 |
-
if (isset($o['info']['procset'])) {
|
700 |
-
$res .= "\n/ProcSet " . $o['info']['procset'] . " 0 R";
|
701 |
-
}
|
702 |
-
|
703 |
-
if (isset($o['info']['fonts']) && count($o['info']['fonts'])) {
|
704 |
-
$res .= "\n/Font << ";
|
705 |
-
foreach ($o['info']['fonts'] as $finfo) {
|
706 |
-
$res .= "\n/F" . $finfo['fontNum'] . " " . $finfo['objNum'] . " 0 R";
|
707 |
-
}
|
708 |
-
$res .= "\n>>";
|
709 |
-
}
|
710 |
-
|
711 |
-
if (isset($o['info']['xObjects']) && count($o['info']['xObjects'])) {
|
712 |
-
$res .= "\n/XObject << ";
|
713 |
-
foreach ($o['info']['xObjects'] as $finfo) {
|
714 |
-
$res .= "\n/" . $finfo['label'] . " " . $finfo['objNum'] . " 0 R";
|
715 |
-
}
|
716 |
-
$res .= "\n>>";
|
717 |
-
}
|
718 |
-
|
719 |
-
if (isset($o['info']['extGStates']) && count($o['info']['extGStates'])) {
|
720 |
-
$res .= "\n/ExtGState << ";
|
721 |
-
foreach ($o['info']['extGStates'] as $gstate) {
|
722 |
-
$res .= "\n/GS" . $gstate['stateNum'] . " " . $gstate['objNum'] . " 0 R";
|
723 |
-
}
|
724 |
-
$res .= "\n>>";
|
725 |
-
}
|
726 |
-
|
727 |
-
$res .= "\n>>";
|
728 |
-
if (isset($o['info']['mediaBox'])) {
|
729 |
-
$tmp = $o['info']['mediaBox'];
|
730 |
-
$res .= "\n/MediaBox [" . sprintf(
|
731 |
-
'%.3F %.3F %.3F %.3F',
|
732 |
-
$tmp[0],
|
733 |
-
$tmp[1],
|
734 |
-
$tmp[2],
|
735 |
-
$tmp[3]
|
736 |
-
) . ']';
|
737 |
-
}
|
738 |
-
}
|
739 |
-
|
740 |
-
$res .= "\n >>\nendobj";
|
741 |
-
} else {
|
742 |
-
$res = "\n$id 0 obj\n<< /Type /Pages\n/Count 0\n>>\nendobj";
|
743 |
-
}
|
744 |
-
|
745 |
-
return $res;
|
746 |
-
}
|
747 |
-
|
748 |
-
return null;
|
749 |
-
}
|
750 |
-
|
751 |
-
/**
|
752 |
-
* define the outlines in the doc, empty for now
|
753 |
-
*
|
754 |
-
* @param $id
|
755 |
-
* @param $action
|
756 |
-
* @param string $options
|
757 |
-
* @return string|null
|
758 |
-
*/
|
759 |
-
protected function o_outlines($id, $action, $options = '')
|
760 |
-
{
|
761 |
-
if ($action !== 'new') {
|
762 |
-
$o = &$this->objects[$id];
|
763 |
-
}
|
764 |
-
|
765 |
-
switch ($action) {
|
766 |
-
case 'new':
|
767 |
-
$this->objects[$id] = array('t' => 'outlines', 'info' => array('outlines' => array()));
|
768 |
-
$this->o_catalog($this->catalogId, 'outlines', $id);
|
769 |
-
break;
|
770 |
-
|
771 |
-
case 'outline':
|
772 |
-
$o['info']['outlines'][] = $options;
|
773 |
-
break;
|
774 |
-
|
775 |
-
case 'out':
|
776 |
-
if (count($o['info']['outlines'])) {
|
777 |
-
$res = "\n$id 0 obj\n<< /Type /Outlines /Kids [";
|
778 |
-
foreach ($o['info']['outlines'] as $v) {
|
779 |
-
$res .= "$v 0 R ";
|
780 |
-
}
|
781 |
-
|
782 |
-
$res .= "] /Count " . count($o['info']['outlines']) . " >>\nendobj";
|
783 |
-
} else {
|
784 |
-
$res = "\n$id 0 obj\n<< /Type /Outlines /Count 0 >>\nendobj";
|
785 |
-
}
|
786 |
-
|
787 |
-
return $res;
|
788 |
-
}
|
789 |
-
|
790 |
-
return null;
|
791 |
-
}
|
792 |
-
|
793 |
-
/**
|
794 |
-
* an object to hold the font description
|
795 |
-
*
|
796 |
-
* @param $id
|
797 |
-
* @param $action
|
798 |
-
* @param string|array $options
|
799 |
-
* @return string|null
|
800 |
-
*/
|
801 |
-
protected function o_font($id, $action, $options = '')
|
802 |
-
{
|
803 |
-
if ($action !== 'new') {
|
804 |
-
$o = &$this->objects[$id];
|
805 |
-
}
|
806 |
-
|
807 |
-
switch ($action) {
|
808 |
-
case 'new':
|
809 |
-
$this->objects[$id] = array(
|
810 |
-
't' => 'font',
|
811 |
-
'info' => array(
|
812 |
-
'name' => $options['name'],
|
813 |
-
'fontFileName' => $options['fontFileName'],
|
814 |
-
'SubType' => 'Type1'
|
815 |
-
)
|
816 |
-
);
|
817 |
-
$fontNum = $this->numFonts;
|
818 |
-
$this->objects[$id]['info']['fontNum'] = $fontNum;
|
819 |
-
|
820 |
-
// deal with the encoding and the differences
|
821 |
-
if (isset($options['differences'])) {
|
822 |
-
// then we'll need an encoding dictionary
|
823 |
-
$this->numObj++;
|
824 |
-
$this->o_fontEncoding($this->numObj, 'new', $options);
|
825 |
-
$this->objects[$id]['info']['encodingDictionary'] = $this->numObj;
|
826 |
-
} else {
|
827 |
-
if (isset($options['encoding'])) {
|
828 |
-
// we can specify encoding here
|
829 |
-
switch ($options['encoding']) {
|
830 |
-
case 'WinAnsiEncoding':
|
831 |
-
case 'MacRomanEncoding':
|
832 |
-
case 'MacExpertEncoding':
|
833 |
-
$this->objects[$id]['info']['encoding'] = $options['encoding'];
|
834 |
-
break;
|
835 |
-
|
836 |
-
case 'none':
|
837 |
-
break;
|
838 |
-
|
839 |
-
default:
|
840 |
-
$this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
|
841 |
-
break;
|
842 |
-
}
|
843 |
-
} else {
|
844 |
-
$this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
|
845 |
-
}
|
846 |
-
}
|
847 |
-
|
848 |
-
if ($this->fonts[$options['fontFileName']]['isUnicode']) {
|
849 |
-
// For Unicode fonts, we need to incorporate font data into
|
850 |
-
// sub-sections that are linked from the primary font section.
|
851 |
-
// Look at o_fontGIDtoCID and o_fontDescendentCID functions
|
852 |
-
// for more information.
|
853 |
-
//
|
854 |
-
// All of this code is adapted from the excellent changes made to
|
855 |
-
// transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
|
856 |
-
|
857 |
-
$toUnicodeId = ++$this->numObj;
|
858 |
-
$this->o_toUnicode($toUnicodeId, 'new');
|
859 |
-
$this->objects[$id]['info']['toUnicode'] = $toUnicodeId;
|
860 |
-
|
861 |
-
$cidFontId = ++$this->numObj;
|
862 |
-
$this->o_fontDescendentCID($cidFontId, 'new', $options);
|
863 |
-
$this->objects[$id]['info']['cidFont'] = $cidFontId;
|
864 |
-
}
|
865 |
-
|
866 |
-
// also tell the pages node about the new font
|
867 |
-
$this->o_pages($this->currentNode, 'font', array('fontNum' => $fontNum, 'objNum' => $id));
|
868 |
-
break;
|
869 |
-
|
870 |
-
case 'add':
|
871 |
-
foreach ($options as $k => $v) {
|
872 |
-
switch ($k) {
|
873 |
-
case 'BaseFont':
|
874 |
-
$o['info']['name'] = $v;
|
875 |
-
break;
|
876 |
-
case 'FirstChar':
|
877 |
-
case 'LastChar':
|
878 |
-
case 'Widths':
|
879 |
-
case 'FontDescriptor':
|
880 |
-
case 'SubType':
|
881 |
-
$this->addMessage('o_font ' . $k . " : " . $v);
|
882 |
-
$o['info'][$k] = $v;
|
883 |
-
break;
|
884 |
-
}
|
885 |
-
}
|
886 |
-
|
887 |
-
// pass values down to descendent font
|
888 |
-
if (isset($o['info']['cidFont'])) {
|
889 |
-
$this->o_fontDescendentCID($o['info']['cidFont'], 'add', $options);
|
890 |
-
}
|
891 |
-
break;
|
892 |
-
|
893 |
-
case 'out':
|
894 |
-
if ($this->fonts[$this->objects[$id]['info']['fontFileName']]['isUnicode']) {
|
895 |
-
// For Unicode fonts, we need to incorporate font data into
|
896 |
-
// sub-sections that are linked from the primary font section.
|
897 |
-
// Look at o_fontGIDtoCID and o_fontDescendentCID functions
|
898 |
-
// for more information.
|
899 |
-
//
|
900 |
-
// All of this code is adapted from the excellent changes made to
|
901 |
-
// transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
|
902 |
-
|
903 |
-
$res = "\n$id 0 obj\n<</Type /Font\n/Subtype /Type0\n";
|
904 |
-
$res .= "/BaseFont /" . $o['info']['name'] . "\n";
|
905 |
-
|
906 |
-
// The horizontal identity mapping for 2-byte CIDs; may be used
|
907 |
-
// with CIDFonts using any Registry, Ordering, and Supplement values.
|
908 |
-
$res .= "/Encoding /Identity-H\n";
|
909 |
-
$res .= "/DescendantFonts [" . $o['info']['cidFont'] . " 0 R]\n";
|
910 |
-
$res .= "/ToUnicode " . $o['info']['toUnicode'] . " 0 R\n";
|
911 |
-
$res .= ">>\n";
|
912 |
-
$res .= "endobj";
|
913 |
-
} else {
|
914 |
-
$res = "\n$id 0 obj\n<< /Type /Font\n/Subtype /" . $o['info']['SubType'] . "\n";
|
915 |
-
$res .= "/Name /F" . $o['info']['fontNum'] . "\n";
|
916 |
-
$res .= "/BaseFont /" . $o['info']['name'] . "\n";
|
917 |
-
|
918 |
-
if (isset($o['info']['encodingDictionary'])) {
|
919 |
-
// then place a reference to the dictionary
|
920 |
-
$res .= "/Encoding " . $o['info']['encodingDictionary'] . " 0 R\n";
|
921 |
-
} else {
|
922 |
-
if (isset($o['info']['encoding'])) {
|
923 |
-
// use the specified encoding
|
924 |
-
$res .= "/Encoding /" . $o['info']['encoding'] . "\n";
|
925 |
-
}
|
926 |
-
}
|
927 |
-
|
928 |
-
if (isset($o['info']['FirstChar'])) {
|
929 |
-
$res .= "/FirstChar " . $o['info']['FirstChar'] . "\n";
|
930 |
-
}
|
931 |
-
|
932 |
-
if (isset($o['info']['LastChar'])) {
|
933 |
-
$res .= "/LastChar " . $o['info']['LastChar'] . "\n";
|
934 |
-
}
|
935 |
-
|
936 |
-
if (isset($o['info']['Widths'])) {
|
937 |
-
$res .= "/Widths " . $o['info']['Widths'] . " 0 R\n";
|
938 |
-
}
|
939 |
-
|
940 |
-
if (isset($o['info']['FontDescriptor'])) {
|
941 |
-
$res .= "/FontDescriptor " . $o['info']['FontDescriptor'] . " 0 R\n";
|
942 |
-
}
|
943 |
-
|
944 |
-
$res .= ">>\n";
|
945 |
-
$res .= "endobj";
|
946 |
-
}
|
947 |
-
|
948 |
-
return $res;
|
949 |
-
}
|
950 |
-
|
951 |
-
return null;
|
952 |
-
}
|
953 |
-
|
954 |
-
/**
|
955 |
-
* A toUnicode section, needed for unicode fonts
|
956 |
-
*
|
957 |
-
* @param $id
|
958 |
-
* @param $action
|
959 |
-
* @return null|string
|
960 |
-
*/
|
961 |
-
protected function o_toUnicode($id, $action)
|
962 |
-
{
|
963 |
-
switch ($action) {
|
964 |
-
case 'new':
|
965 |
-
$this->objects[$id] = array(
|
966 |
-
't' => 'toUnicode'
|
967 |
-
);
|
968 |
-
break;
|
969 |
-
case 'add':
|
970 |
-
break;
|
971 |
-
case 'out':
|
972 |
-
$ordering = '(UCS)';
|
973 |
-
$registry = '(Adobe)';
|
974 |
-
|
975 |
-
if ($this->encrypted) {
|
976 |
-
$this->encryptInit($id);
|
977 |
-
$ordering = $this->ARC4($ordering);
|
978 |
-
$registry = $this->ARC4($registry);
|
979 |
-
}
|
980 |
-
|
981 |
-
$stream = <<<EOT
|
982 |
-
/CIDInit /ProcSet findresource begin
|
983 |
-
12 dict begin
|
984 |
-
begincmap
|
985 |
-
/CIDSystemInfo
|
986 |
-
<</Registry $registry
|
987 |
-
/Ordering $ordering
|
988 |
-
/Supplement 0
|
989 |
-
>> def
|
990 |
-
/CMapName /Adobe-Identity-UCS def
|
991 |
-
/CMapType 2 def
|
992 |
-
1 begincodespacerange
|
993 |
-
<0000> <FFFF>
|
994 |
-
endcodespacerange
|
995 |
-
1 beginbfrange
|
996 |
-
<0000> <FFFF> <0000>
|
997 |
-
endbfrange
|
998 |
-
endcmap
|
999 |
-
CMapName currentdict /CMap defineresource pop
|
1000 |
-
end
|
1001 |
-
end
|
1002 |
-
EOT;
|
1003 |
-
|
1004 |
-
$res = "\n$id 0 obj\n";
|
1005 |
-
$res .= "<</Length " . mb_strlen($stream, '8bit') . " >>\n";
|
1006 |
-
$res .= "stream\n" . $stream . "\nendstream" . "\nendobj";;
|
1007 |
-
|
1008 |
-
return $res;
|
1009 |
-
}
|
1010 |
-
|
1011 |
-
return null;
|
1012 |
-
}
|
1013 |
-
|
1014 |
-
/**
|
1015 |
-
* a font descriptor, needed for including additional fonts
|
1016 |
-
*
|
1017 |
-
* @param $id
|
1018 |
-
* @param $action
|
1019 |
-
* @param string $options
|
1020 |
-
* @return null|string
|
1021 |
-
*/
|
1022 |
-
protected function o_fontDescriptor($id, $action, $options = '')
|
1023 |
-
{
|
1024 |
-
if ($action !== 'new') {
|
1025 |
-
$o = &$this->objects[$id];
|
1026 |
-
}
|
1027 |
-
|
1028 |
-
switch ($action) {
|
1029 |
-
case 'new':
|
1030 |
-
$this->objects[$id] = array('t' => 'fontDescriptor', 'info' => $options);
|
1031 |
-
break;
|
1032 |
-
|
1033 |
-
case 'out':
|
1034 |
-
$res = "\n$id 0 obj\n<< /Type /FontDescriptor\n";
|
1035 |
-
foreach ($o['info'] as $label => $value) {
|
1036 |
-
switch ($label) {
|
1037 |
-
case 'Ascent':
|
1038 |
-
case 'CapHeight':
|
1039 |
-
case 'Descent':
|
1040 |
-
case 'Flags':
|
1041 |
-
case 'ItalicAngle':
|
1042 |
-
case 'StemV':
|
1043 |
-
case 'AvgWidth':
|
1044 |
-
case 'Leading':
|
1045 |
-
case 'MaxWidth':
|
1046 |
-
case 'MissingWidth':
|
1047 |
-
case 'StemH':
|
1048 |
-
case 'XHeight':
|
1049 |
-
case 'CharSet':
|
1050 |
-
if (mb_strlen($value, '8bit')) {
|
1051 |
-
$res .= "/$label $value\n";
|
1052 |
-
}
|
1053 |
-
|
1054 |
-
break;
|
1055 |
-
case 'FontFile':
|
1056 |
-
case 'FontFile2':
|
1057 |
-
case 'FontFile3':
|
1058 |
-
$res .= "/$label $value 0 R\n";
|
1059 |
-
break;
|
1060 |
-
|
1061 |
-
case 'FontBBox':
|
1062 |
-
$res .= "/$label [$value[0] $value[1] $value[2] $value[3]]\n";
|
1063 |
-
break;
|
1064 |
-
|
1065 |
-
case 'FontName':
|
1066 |
-
$res .= "/$label /$value\n";
|
1067 |
-
break;
|
1068 |
-
}
|
1069 |
-
}
|
1070 |
-
|
1071 |
-
$res .= ">>\nendobj";
|
1072 |
-
|
1073 |
-
return $res;
|
1074 |
-
}
|
1075 |
-
|
1076 |
-
return null;
|
1077 |
-
}
|
1078 |
-
|
1079 |
-
/**
|
1080 |
-
* the font encoding
|
1081 |
-
*
|
1082 |
-
* @param $id
|
1083 |
-
* @param $action
|
1084 |
-
* @param string $options
|
1085 |
-
* @return null|string
|
1086 |
-
*/
|
1087 |
-
protected function o_fontEncoding($id, $action, $options = '')
|
1088 |
-
{
|
1089 |
-
if ($action !== 'new') {
|
1090 |
-
$o = &$this->objects[$id];
|
1091 |
-
}
|
1092 |
-
|
1093 |
-
switch ($action) {
|
1094 |
-
case 'new':
|
1095 |
-
// the options array should contain 'differences' and maybe 'encoding'
|
1096 |
-
$this->objects[$id] = array('t' => 'fontEncoding', 'info' => $options);
|
1097 |
-
break;
|
1098 |
-
|
1099 |
-
case 'out':
|
1100 |
-
$res = "\n$id 0 obj\n<< /Type /Encoding\n";
|
1101 |
-
if (!isset($o['info']['encoding'])) {
|
1102 |
-
$o['info']['encoding'] = 'WinAnsiEncoding';
|
1103 |
-
}
|
1104 |
-
|
1105 |
-
if ($o['info']['encoding'] !== 'none') {
|
1106 |
-
$res .= "/BaseEncoding /" . $o['info']['encoding'] . "\n";
|
1107 |
-
}
|
1108 |
-
|
1109 |
-
$res .= "/Differences \n[";
|
1110 |
-
|
1111 |
-
$onum = -100;
|
1112 |
-
|
1113 |
-
foreach ($o['info']['differences'] as $num => $label) {
|
1114 |
-
if ($num != $onum + 1) {
|
1115 |
-
// we cannot make use of consecutive numbering
|
1116 |
-
$res .= "\n$num /$label";
|
1117 |
-
} else {
|
1118 |
-
$res .= " /$label";
|
1119 |
-
}
|
1120 |
-
|
1121 |
-
$onum = $num;
|
1122 |
-
}
|
1123 |
-
|
1124 |
-
$res .= "\n]\n>>\nendobj";
|
1125 |
-
|
1126 |
-
return $res;
|
1127 |
-
}
|
1128 |
-
|
1129 |
-
return null;
|
1130 |
-
}
|
1131 |
-
|
1132 |
-
/**
|
1133 |
-
* a descendent cid font, needed for unicode fonts
|
1134 |
-
*
|
1135 |
-
* @param $id
|
1136 |
-
* @param $action
|
1137 |
-
* @param string|array $options
|
1138 |
-
* @return null|string
|
1139 |
-
*/
|
1140 |
-
protected function o_fontDescendentCID($id, $action, $options = '')
|
1141 |
-
{
|
1142 |
-
if ($action !== 'new') {
|
1143 |
-
$o = &$this->objects[$id];
|
1144 |
-
}
|
1145 |
-
|
1146 |
-
switch ($action) {
|
1147 |
-
case 'new':
|
1148 |
-
$this->objects[$id] = array('t' => 'fontDescendentCID', 'info' => $options);
|
1149 |
-
|
1150 |
-
// we need a CID system info section
|
1151 |
-
$cidSystemInfoId = ++$this->numObj;
|
1152 |
-
$this->o_cidSystemInfo($cidSystemInfoId, 'new');
|
1153 |
-
$this->objects[$id]['info']['cidSystemInfo'] = $cidSystemInfoId;
|
1154 |
-
|
1155 |
-
// and a CID to GID map
|
1156 |
-
$cidToGidMapId = ++$this->numObj;
|
1157 |
-
$this->o_fontGIDtoCIDMap($cidToGidMapId, 'new', $options);
|
1158 |
-
$this->objects[$id]['info']['cidToGidMap'] = $cidToGidMapId;
|
1159 |
-
break;
|
1160 |
-
|
1161 |
-
case 'add':
|
1162 |
-
foreach ($options as $k => $v) {
|
1163 |
-
switch ($k) {
|
1164 |
-
case 'BaseFont':
|
1165 |
-
$o['info']['name'] = $v;
|
1166 |
-
break;
|
1167 |
-
|
1168 |
-
case 'FirstChar':
|
1169 |
-
case 'LastChar':
|
1170 |
-
case 'MissingWidth':
|
1171 |
-
case 'FontDescriptor':
|
1172 |
-
case 'SubType':
|
1173 |
-
$this->addMessage("o_fontDescendentCID $k : $v");
|
1174 |
-
$o['info'][$k] = $v;
|
1175 |
-
break;
|
1176 |
-
}
|
1177 |
-
}
|
1178 |
-
|
1179 |
-
// pass values down to cid to gid map
|
1180 |
-
$this->o_fontGIDtoCIDMap($o['info']['cidToGidMap'], 'add', $options);
|
1181 |
-
break;
|
1182 |
-
|
1183 |
-
case 'out':
|
1184 |
-
$res = "\n$id 0 obj\n";
|
1185 |
-
$res .= "<</Type /Font\n";
|
1186 |
-
$res .= "/Subtype /CIDFontType2\n";
|
1187 |
-
$res .= "/BaseFont /" . $o['info']['name'] . "\n";
|
1188 |
-
$res .= "/CIDSystemInfo " . $o['info']['cidSystemInfo'] . " 0 R\n";
|
1189 |
-
// if (isset($o['info']['FirstChar'])) {
|
1190 |
-
// $res.= "/FirstChar ".$o['info']['FirstChar']."\n";
|
1191 |
-
// }
|
1192 |
-
|
1193 |
-
// if (isset($o['info']['LastChar'])) {
|
1194 |
-
// $res.= "/LastChar ".$o['info']['LastChar']."\n";
|
1195 |
-
// }
|
1196 |
-
if (isset($o['info']['FontDescriptor'])) {
|
1197 |
-
$res .= "/FontDescriptor " . $o['info']['FontDescriptor'] . " 0 R\n";
|
1198 |
-
}
|
1199 |
-
|
1200 |
-
if (isset($o['info']['MissingWidth'])) {
|
1201 |
-
$res .= "/DW " . $o['info']['MissingWidth'] . "\n";
|
1202 |
-
}
|
1203 |
-
|
1204 |
-
if (isset($o['info']['fontFileName']) && isset($this->fonts[$o['info']['fontFileName']]['CIDWidths'])) {
|
1205 |
-
$cid_widths = &$this->fonts[$o['info']['fontFileName']]['CIDWidths'];
|
1206 |
-
$w = '';
|
1207 |
-
foreach ($cid_widths as $cid => $width) {
|
1208 |
-
$w .= "$cid [$width] ";
|
1209 |
-
}
|
1210 |
-
$res .= "/W [$w]\n";
|
1211 |
-
}
|
1212 |
-
|
1213 |
-
$res .= "/CIDToGIDMap " . $o['info']['cidToGidMap'] . " 0 R\n";
|
1214 |
-
$res .= ">>\n";
|
1215 |
-
$res .= "endobj";
|
1216 |
-
|
1217 |
-
return $res;
|
1218 |
-
}
|
1219 |
-
|
1220 |
-
return null;
|
1221 |
-
}
|
1222 |
-
|
1223 |
-
/**
|
1224 |
-
* CID system info section, needed for unicode fonts
|
1225 |
-
*
|
1226 |
-
* @param $id
|
1227 |
-
* @param $action
|
1228 |
-
* @return null|string
|
1229 |
-
*/
|
1230 |
-
protected function o_cidSystemInfo($id, $action)
|
1231 |
-
{
|
1232 |
-
switch ($action) {
|
1233 |
-
case 'new':
|
1234 |
-
$this->objects[$id] = array(
|
1235 |
-
't' => 'cidSystemInfo'
|
1236 |
-
);
|
1237 |
-
break;
|
1238 |
-
case 'add':
|
1239 |
-
break;
|
1240 |
-
case 'out':
|
1241 |
-
$ordering = '(UCS)';
|
1242 |
-
$registry = '(Adobe)';
|
1243 |
-
|
1244 |
-
if ($this->encrypted) {
|
1245 |
-
$this->encryptInit($id);
|
1246 |
-
$ordering = $this->ARC4($ordering);
|
1247 |
-
$registry = $this->ARC4($registry);
|
1248 |
-
}
|
1249 |
-
|
1250 |
-
|
1251 |
-
$res = "\n$id 0 obj\n";
|
1252 |
-
|
1253 |
-
$res .= '<</Registry ' . $registry . "\n"; // A string identifying an issuer of character collections
|
1254 |
-
$res .= '/Ordering ' . $ordering . "\n"; // A string that uniquely names a character collection issued by a specific registry
|
1255 |
-
$res .= "/Supplement 0\n"; // The supplement number of the character collection.
|
1256 |
-
$res .= ">>";
|
1257 |
-
|
1258 |
-
$res .= "\nendobj";;
|
1259 |
-
|
1260 |
-
return $res;
|
1261 |
-
}
|
1262 |
-
|
1263 |
-
return null;
|
1264 |
-
}
|
1265 |
-
|
1266 |
-
/**
|
1267 |
-
* a font glyph to character map, needed for unicode fonts
|
1268 |
-
*
|
1269 |
-
* @param $id
|
1270 |
-
* @param $action
|
1271 |
-
* @param string $options
|
1272 |
-
* @return null|string
|
1273 |
-
*/
|
1274 |
-
protected function o_fontGIDtoCIDMap($id, $action, $options = '')
|
1275 |
-
{
|
1276 |
-
if ($action !== 'new') {
|
1277 |
-
$o = &$this->objects[$id];
|
1278 |
-
}
|
1279 |
-
|
1280 |
-
switch ($action) {
|
1281 |
-
case 'new':
|
1282 |
-
$this->objects[$id] = array('t' => 'fontGIDtoCIDMap', 'info' => $options);
|
1283 |
-
break;
|
1284 |
-
|
1285 |
-
case 'out':
|
1286 |
-
$res = "\n$id 0 obj\n";
|
1287 |
-
$fontFileName = $o['info']['fontFileName'];
|
1288 |
-
$tmp = $this->fonts[$fontFileName]['CIDtoGID'] = base64_decode($this->fonts[$fontFileName]['CIDtoGID']);
|
1289 |
-
|
1290 |
-
$compressed = isset($this->fonts[$fontFileName]['CIDtoGID_Compressed']) &&
|
1291 |
-
$this->fonts[$fontFileName]['CIDtoGID_Compressed'];
|
1292 |
-
|
1293 |
-
if (!$compressed && isset($o['raw'])) {
|
1294 |
-
$res .= $tmp;
|
1295 |
-
} else {
|
1296 |
-
$res .= "<<";
|
1297 |
-
|
1298 |
-
if (!$compressed && $this->compressionReady && $this->options['compression']) {
|
1299 |
-
// then implement ZLIB based compression on this content stream
|
1300 |
-
$compressed = true;
|
1301 |
-
$tmp = gzcompress($tmp, 6);
|
1302 |
-
}
|
1303 |
-
if ($compressed) {
|
1304 |
-
$res .= "\n/Filter /FlateDecode";
|
1305 |
-
}
|
1306 |
-
|
1307 |
-
if ($this->encrypted) {
|
1308 |
-
$this->encryptInit($id);
|
1309 |
-
$tmp = $this->ARC4($tmp);
|
1310 |
-
}
|
1311 |
-
|
1312 |
-
$res .= "\n/Length " . mb_strlen($tmp, '8bit') . ">>\nstream\n$tmp\nendstream";
|
1313 |
-
}
|
1314 |
-
|
1315 |
-
$res .= "\nendobj";
|
1316 |
-
|
1317 |
-
return $res;
|
1318 |
-
}
|
1319 |
-
|
1320 |
-
return null;
|
1321 |
-
}
|
1322 |
-
|
1323 |
-
/**
|
1324 |
-
* the document procset, solves some problems with printing to old PS printers
|
1325 |
-
*
|
1326 |
-
* @param $id
|
1327 |
-
* @param $action
|
1328 |
-
* @param string $options
|
1329 |
-
* @return null|string
|
1330 |
-
*/
|
1331 |
-
protected function o_procset($id, $action, $options = '')
|
1332 |
-
{
|
1333 |
-
if ($action !== 'new') {
|
1334 |
-
$o = &$this->objects[$id];
|
1335 |
-
}
|
1336 |
-
|
1337 |
-
switch ($action) {
|
1338 |
-
case 'new':
|
1339 |
-
$this->objects[$id] = array('t' => 'procset', 'info' => array('PDF' => 1, 'Text' => 1));
|
1340 |
-
$this->o_pages($this->currentNode, 'procset', $id);
|
1341 |
-
$this->procsetObjectId = $id;
|
1342 |
-
break;
|
1343 |
-
|
1344 |
-
case 'add':
|
1345 |
-
// this is to add new items to the procset list, despite the fact that this is considered
|
1346 |
-
// obsolete, the items are required for printing to some postscript printers
|
1347 |
-
switch ($options) {
|
1348 |
-
case 'ImageB':
|
1349 |
-
case 'ImageC':
|
1350 |
-
case 'ImageI':
|
1351 |
-
$o['info'][$options] = 1;
|
1352 |
-
break;
|
1353 |
-
}
|
1354 |
-
break;
|
1355 |
-
|
1356 |
-
case 'out':
|
1357 |
-
$res = "\n$id 0 obj\n[";
|
1358 |
-
foreach ($o['info'] as $label => $val) {
|
1359 |
-
$res .= "/$label ";
|
1360 |
-
}
|
1361 |
-
$res .= "]\nendobj";
|
1362 |
-
|
1363 |
-
return $res;
|
1364 |
-
}
|
1365 |
-
|
1366 |
-
return null;
|
1367 |
-
}
|
1368 |
-
|
1369 |
-
/**
|
1370 |
-
* define the document information
|
1371 |
-
*
|
1372 |
-
* @param $id
|
1373 |
-
* @param $action
|
1374 |
-
* @param string $options
|
1375 |
-
* @return null|string
|
1376 |
-
*/
|
1377 |
-
protected function o_info($id, $action, $options = '')
|
1378 |
-
{
|
1379 |
-
switch ($action) {
|
1380 |
-
case 'new':
|
1381 |
-
$this->infoObject = $id;
|
1382 |
-
$date = 'D:' . @date('Ymd');
|
1383 |
-
$this->objects[$id] = array(
|
1384 |
-
't' => 'info',
|
1385 |
-
'info' => array(
|
1386 |
-
'Producer' => 'CPDF (dompdf)',
|
1387 |
-
'CreationDate' => $date
|
1388 |
-
)
|
1389 |
-
);
|
1390 |
-
break;
|
1391 |
-
case 'Title':
|
1392 |
-
case 'Author':
|
1393 |
-
case 'Subject':
|
1394 |
-
case 'Keywords':
|
1395 |
-
case 'Creator':
|
1396 |
-
case 'Producer':
|
1397 |
-
case 'CreationDate':
|
1398 |
-
case 'ModDate':
|
1399 |
-
case 'Trapped':
|
1400 |
-
$this->objects[$id]['info'][$action] = $options;
|
1401 |
-
break;
|
1402 |
-
|
1403 |
-
case 'out':
|
1404 |
-
$encrypted = $this->encrypted;
|
1405 |
-
if ($encrypted) {
|
1406 |
-
$this->encryptInit($id);
|
1407 |
-
}
|
1408 |
-
|
1409 |
-
$res = "\n$id 0 obj\n<<\n";
|
1410 |
-
$o = &$this->objects[$id];
|
1411 |
-
foreach ($o['info'] as $k => $v) {
|
1412 |
-
$res .= "/$k (";
|
1413 |
-
|
1414 |
-
// dates must be outputted as-is, without Unicode transformations
|
1415 |
-
if ($k !== 'CreationDate' && $k !== 'ModDate') {
|
1416 |
-
$v = $this->filterText($v, true, false);
|
1417 |
-
}
|
1418 |
-
|
1419 |
-
if ($encrypted) {
|
1420 |
-
$v = $this->ARC4($v);
|
1421 |
-
}
|
1422 |
-
|
1423 |
-
$res .= $v;
|
1424 |
-
$res .= ")\n";
|
1425 |
-
}
|
1426 |
-
|
1427 |
-
$res .= ">>\nendobj";
|
1428 |
-
|
1429 |
-
return $res;
|
1430 |
-
}
|
1431 |
-
|
1432 |
-
return null;
|
1433 |
-
}
|
1434 |
-
|
1435 |
-
/**
|
1436 |
-
* an action object, used to link to URLS initially
|
1437 |
-
*
|
1438 |
-
* @param $id
|
1439 |
-
* @param $action
|
1440 |
-
* @param string $options
|
1441 |
-
* @return null|string
|
1442 |
-
*/
|
1443 |
-
protected function o_action($id, $action, $options = '')
|
1444 |
-
{
|
1445 |
-
if ($action !== 'new') {
|
1446 |
-
$o = &$this->objects[$id];
|
1447 |
-
}
|
1448 |
-
|
1449 |
-
switch ($action) {
|
1450 |
-
case 'new':
|
1451 |
-
if (is_array($options)) {
|
1452 |
-
$this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => $options['type']);
|
1453 |
-
} else {
|
1454 |
-
// then assume a URI action
|
1455 |
-
$this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => 'URI');
|
1456 |
-
}
|
1457 |
-
break;
|
1458 |
-
|
1459 |
-
case 'out':
|
1460 |
-
if ($this->encrypted) {
|
1461 |
-
$this->encryptInit($id);
|
1462 |
-
}
|
1463 |
-
|
1464 |
-
$res = "\n$id 0 obj\n<< /Type /Action";
|
1465 |
-
switch ($o['type']) {
|
1466 |
-
case 'ilink':
|
1467 |
-
if (!isset($this->destinations[(string)$o['info']['label']])) {
|
1468 |
-
break;
|
1469 |
-
}
|
1470 |
-
|
1471 |
-
// there will be an 'label' setting, this is the name of the destination
|
1472 |
-
$res .= "\n/S /GoTo\n/D " . $this->destinations[(string)$o['info']['label']] . " 0 R";
|
1473 |
-
break;
|
1474 |
-
|
1475 |
-
case 'URI':
|
1476 |
-
$res .= "\n/S /URI\n/URI (";
|
1477 |
-
if ($this->encrypted) {
|
1478 |
-
$res .= $this->filterText($this->ARC4($o['info']), false, false);
|
1479 |
-
} else {
|
1480 |
-
$res .= $this->filterText($o['info'], false, false);
|
1481 |
-
}
|
1482 |
-
|
1483 |
-
$res .= ")";
|
1484 |
-
break;
|
1485 |
-
}
|
1486 |
-
|
1487 |
-
$res .= "\n>>\nendobj";
|
1488 |
-
|
1489 |
-
return $res;
|
1490 |
-
}
|
1491 |
-
|
1492 |
-
return null;
|
1493 |
-
}
|
1494 |
-
|
1495 |
-
/**
|
1496 |
-
* an annotation object, this will add an annotation to the current page.
|
1497 |
-
* initially will support just link annotations
|
1498 |
-
*
|
1499 |
-
* @param $id
|
1500 |
-
* @param $action
|
1501 |
-
* @param string $options
|
1502 |
-
* @return null|string
|
1503 |
-
*/
|
1504 |
-
protected function o_annotation($id, $action, $options = '')
|
1505 |
-
{
|
1506 |
-
if ($action !== 'new') {
|
1507 |
-
$o = &$this->objects[$id];
|
1508 |
-
}
|
1509 |
-
|
1510 |
-
switch ($action) {
|
1511 |
-
case 'new':
|
1512 |
-
// add the annotation to the current page
|
1513 |
-
$pageId = $this->currentPage;
|
1514 |
-
$this->o_page($pageId, 'annot', $id);
|
1515 |
-
|
1516 |
-
// and add the action object which is going to be required
|
1517 |
-
switch ($options['type']) {
|
1518 |
-
case 'link':
|
1519 |
-
$this->objects[$id] = array('t' => 'annotation', 'info' => $options);
|
1520 |
-
$this->numObj++;
|
1521 |
-
$this->o_action($this->numObj, 'new', $options['url']);
|
1522 |
-
$this->objects[$id]['info']['actionId'] = $this->numObj;
|
1523 |
-
break;
|
1524 |
-
|
1525 |
-
case 'ilink':
|
1526 |
-
// this is to a named internal link
|
1527 |
-
$label = $options['label'];
|
1528 |
-
$this->objects[$id] = array('t' => 'annotation', 'info' => $options);
|
1529 |
-
$this->numObj++;
|
1530 |
-
$this->o_action($this->numObj, 'new', array('type' => 'ilink', 'label' => $label));
|
1531 |
-
$this->objects[$id]['info']['actionId'] = $this->numObj;
|
1532 |
-
break;
|
1533 |
-
}
|
1534 |
-
break;
|
1535 |
-
|
1536 |
-
case 'out':
|
1537 |
-
$res = "\n$id 0 obj\n<< /Type /Annot";
|
1538 |
-
switch ($o['info']['type']) {
|
1539 |
-
case 'link':
|
1540 |
-
case 'ilink':
|
1541 |
-
$res .= "\n/Subtype /Link";
|
1542 |
-
break;
|
1543 |
-
}
|
1544 |
-
$res .= "\n/A " . $o['info']['actionId'] . " 0 R";
|
1545 |
-
$res .= "\n/Border [0 0 0]";
|
1546 |
-
$res .= "\n/H /I";
|
1547 |
-
$res .= "\n/Rect [ ";
|
1548 |
-
|
1549 |
-
foreach ($o['info']['rect'] as $v) {
|
1550 |
-
$res .= sprintf("%.4F ", $v);
|
1551 |
-
}
|
1552 |
-
|
1553 |
-
$res .= "]";
|
1554 |
-
$res .= "\n>>\nendobj";
|
1555 |
-
|
1556 |
-
return $res;
|
1557 |
-
}
|
1558 |
-
|
1559 |
-
return null;
|
1560 |
-
}
|
1561 |
-
|
1562 |
-
/**
|
1563 |
-
* a page object, it also creates a contents object to hold its contents
|
1564 |
-
*
|
1565 |
-
* @param $id
|
1566 |
-
* @param $action
|
1567 |
-
* @param string $options
|
1568 |
-
* @return null|string
|
1569 |
-
*/
|
1570 |
-
protected function o_page($id, $action, $options = '')
|
1571 |
-
{
|
1572 |
-
if ($action !== 'new') {
|
1573 |
-
$o = &$this->objects[$id];
|
1574 |
-
}
|
1575 |
-
|
1576 |
-
switch ($action) {
|
1577 |
-
case 'new':
|
1578 |
-
$this->numPages++;
|
1579 |
-
$this->objects[$id] = array(
|
1580 |
-
't' => 'page',
|
1581 |
-
'info' => array(
|
1582 |
-
'parent' => $this->currentNode,
|
1583 |
-
'pageNum' => $this->numPages,
|
1584 |
-
'mediaBox' => $this->objects[$this->currentNode]['info']['mediaBox']
|
1585 |
-
)
|
1586 |
-
);
|
1587 |
-
|
1588 |
-
if (is_array($options)) {
|
1589 |
-
// then this must be a page insertion, array should contain 'rid','pos'=[before|after]
|
1590 |
-
$options['id'] = $id;
|
1591 |
-
$this->o_pages($this->currentNode, 'page', $options);
|
1592 |
-
} else {
|
1593 |
-
$this->o_pages($this->currentNode, 'page', $id);
|
1594 |
-
}
|
1595 |
-
|
1596 |
-
$this->currentPage = $id;
|
1597 |
-
//make a contents object to go with this page
|
1598 |
-
$this->numObj++;
|
1599 |
-
$this->o_contents($this->numObj, 'new', $id);
|
1600 |
-
$this->currentContents = $this->numObj;
|
1601 |
-
$this->objects[$id]['info']['contents'] = array();
|
1602 |
-
$this->objects[$id]['info']['contents'][] = $this->numObj;
|
1603 |
-
|
1604 |
-
$match = ($this->numPages % 2 ? 'odd' : 'even');
|
1605 |
-
foreach ($this->addLooseObjects as $oId => $target) {
|
1606 |
-
if ($target === 'all' || $match === $target) {
|
1607 |
-
$this->objects[$id]['info']['contents'][] = $oId;
|
1608 |
-
}
|
1609 |
-
}
|
1610 |
-
break;
|
1611 |
-
|
1612 |
-
case 'content':
|
1613 |
-
$o['info']['contents'][] = $options;
|
1614 |
-
break;
|
1615 |
-
|
1616 |
-
case 'annot':
|
1617 |
-
// add an annotation to this page
|
1618 |
-
if (!isset($o['info']['annot'])) {
|
1619 |
-
$o['info']['annot'] = array();
|
1620 |
-
}
|
1621 |
-
|
1622 |
-
// $options should contain the id of the annotation dictionary
|
1623 |
-
$o['info']['annot'][] = $options;
|
1624 |
-
break;
|
1625 |
-
|
1626 |
-
case 'out':
|
1627 |
-
$res = "\n$id 0 obj\n<< /Type /Page";
|
1628 |
-
if (isset($o['info']['mediaBox'])) {
|
1629 |
-
$tmp = $o['info']['mediaBox'];
|
1630 |
-
$res .= "\n/MediaBox [" . sprintf(
|
1631 |
-
'%.3F %.3F %.3F %.3F',
|
1632 |
-
$tmp[0],
|
1633 |
-
$tmp[1],
|
1634 |
-
$tmp[2],
|
1635 |
-
$tmp[3]
|
1636 |
-
) . ']';
|
1637 |
-
}
|
1638 |
-
$res .= "\n/Parent " . $o['info']['parent'] . " 0 R";
|
1639 |
-
|
1640 |
-
if (isset($o['info']['annot'])) {
|
1641 |
-
$res .= "\n/Annots [";
|
1642 |
-
foreach ($o['info']['annot'] as $aId) {
|
1643 |
-
$res .= " $aId 0 R";
|
1644 |
-
}
|
1645 |
-
$res .= " ]";
|
1646 |
-
}
|
1647 |
-
|
1648 |
-
$count = count($o['info']['contents']);
|
1649 |
-
if ($count == 1) {
|
1650 |
-
$res .= "\n/Contents " . $o['info']['contents'][0] . " 0 R";
|
1651 |
-
} else {
|
1652 |
-
if ($count > 1) {
|
1653 |
-
$res .= "\n/Contents [\n";
|
1654 |
-
|
1655 |
-
// reverse the page contents so added objects are below normal content
|
1656 |
-
//foreach (array_reverse($o['info']['contents']) as $cId) {
|
1657 |
-
// Back to normal now that I've got transparency working --Benj
|
1658 |
-
foreach ($o['info']['contents'] as $cId) {
|
1659 |
-
$res .= "$cId 0 R\n";
|
1660 |
-
}
|
1661 |
-
$res .= "]";
|
1662 |
-
}
|
1663 |
-
}
|
1664 |
-
|
1665 |
-
$res .= "\n>>\nendobj";
|
1666 |
-
|
1667 |
-
return $res;
|
1668 |
-
}
|
1669 |
-
|
1670 |
-
return null;
|
1671 |
-
}
|
1672 |
-
|
1673 |
-
/**
|
1674 |
-
* the contents objects hold all of the content which appears on pages
|
1675 |
-
*
|
1676 |
-
* @param $id
|
1677 |
-
* @param $action
|
1678 |
-
* @param string|array $options
|
1679 |
-
* @return null|string
|
1680 |
-
*/
|
1681 |
-
protected function o_contents($id, $action, $options = '')
|
1682 |
-
{
|
1683 |
-
if ($action !== 'new') {
|
1684 |
-
$o = &$this->objects[$id];
|
1685 |
-
}
|
1686 |
-
|
1687 |
-
switch ($action) {
|
1688 |
-
case 'new':
|
1689 |
-
$this->objects[$id] = array('t' => 'contents', 'c' => '', 'info' => array());
|
1690 |
-
if (mb_strlen($options, '8bit') && intval($options)) {
|
1691 |
-
// then this contents is the primary for a page
|
1692 |
-
$this->objects[$id]['onPage'] = $options;
|
1693 |
-
} else {
|
1694 |
-
if ($options === 'raw') {
|
1695 |
-
// then this page contains some other type of system object
|
1696 |
-
$this->objects[$id]['raw'] = 1;
|
1697 |
-
}
|
1698 |
-
}
|
1699 |
-
break;
|
1700 |
-
|
1701 |
-
case 'add':
|
1702 |
-
// add more options to the declaration
|
1703 |
-
foreach ($options as $k => $v) {
|
1704 |
-
$o['info'][$k] = $v;
|
1705 |
-
}
|
1706 |
-
|
1707 |
-
case 'out':
|
1708 |
-
$tmp = $o['c'];
|
1709 |
-
$res = "\n$id 0 obj\n";
|
1710 |
-
|
1711 |
-
if (isset($this->objects[$id]['raw'])) {
|
1712 |
-
$res .= $tmp;
|
1713 |
-
} else {
|
1714 |
-
$res .= "<<";
|
1715 |
-
if ($this->compressionReady && $this->options['compression']) {
|
1716 |
-
// then implement ZLIB based compression on this content stream
|
1717 |
-
$res .= " /Filter /FlateDecode";
|
1718 |
-
$tmp = gzcompress($tmp, 6);
|
1719 |
-
}
|
1720 |
-
|
1721 |
-
if ($this->encrypted) {
|
1722 |
-
$this->encryptInit($id);
|
1723 |
-
$tmp = $this->ARC4($tmp);
|
1724 |
-
}
|
1725 |
-
|
1726 |
-
foreach ($o['info'] as $k => $v) {
|
1727 |
-
$res .= "\n/$k $v";
|
1728 |
-
}
|
1729 |
-
|
1730 |
-
$res .= "\n/Length " . mb_strlen($tmp, '8bit') . " >>\nstream\n$tmp\nendstream";
|
1731 |
-
}
|
1732 |
-
|
1733 |
-
$res .= "\nendobj";
|
1734 |
-
|
1735 |
-
return $res;
|
1736 |
-
}
|
1737 |
-
|
1738 |
-
return null;
|
1739 |
-
}
|
1740 |
-
|
1741 |
-
/**
|
1742 |
-
* @param $id
|
1743 |
-
* @param $action
|
1744 |
-
* @return string|null
|
1745 |
-
*/
|
1746 |
-
protected function o_embedjs($id, $action)
|
1747 |
-
{
|
1748 |
-
switch ($action) {
|
1749 |
-
case 'new':
|
1750 |
-
$this->objects[$id] = array(
|
1751 |
-
't' => 'embedjs',
|
1752 |
-
'info' => array(
|
1753 |
-
'Names' => '[(EmbeddedJS) ' . ($id + 1) . ' 0 R]'
|
1754 |
-
)
|
1755 |
-
);
|
1756 |
-
break;
|
1757 |
-
|
1758 |
-
case 'out':
|
1759 |
-
$o = &$this->objects[$id];
|
1760 |
-
$res = "\n$id 0 obj\n<< ";
|
1761 |
-
foreach ($o['info'] as $k => $v) {
|
1762 |
-
$res .= "\n/$k $v";
|
1763 |
-
}
|
1764 |
-
$res .= "\n>>\nendobj";
|
1765 |
-
|
1766 |
-
return $res;
|
1767 |
-
}
|
1768 |
-
|
1769 |
-
return null;
|
1770 |
-
}
|
1771 |
-
|
1772 |
-
/**
|
1773 |
-
* @param $id
|
1774 |
-
* @param $action
|
1775 |
-
* @param string $code
|
1776 |
-
* @return null|string
|
1777 |
-
*/
|
1778 |
-
protected function o_javascript($id, $action, $code = '')
|
1779 |
-
{
|
1780 |
-
switch ($action) {
|
1781 |
-
case 'new':
|
1782 |
-
$this->objects[$id] = array(
|
1783 |
-
't' => 'javascript',
|
1784 |
-
'info' => array(
|
1785 |
-
'S' => '/JavaScript',
|
1786 |
-
'JS' => '(' . $this->filterText($code, true, false) . ')',
|
1787 |
-
)
|
1788 |
-
);
|
1789 |
-
break;
|
1790 |
-
|
1791 |
-
case 'out':
|
1792 |
-
$o = &$this->objects[$id];
|
1793 |
-
$res = "\n$id 0 obj\n<< ";
|
1794 |
-
|
1795 |
-
foreach ($o['info'] as $k => $v) {
|
1796 |
-
$res .= "\n/$k $v";
|
1797 |
-
}
|
1798 |
-
$res .= "\n>>\nendobj";
|
1799 |
-
|
1800 |
-
return $res;
|
1801 |
-
}
|
1802 |
-
|
1803 |
-
return null;
|
1804 |
-
}
|
1805 |
-
|
1806 |
-
/**
|
1807 |
-
* an image object, will be an XObject in the document, includes description and data
|
1808 |
-
*
|
1809 |
-
* @param $id
|
1810 |
-
* @param $action
|
1811 |
-
* @param string $options
|
1812 |
-
* @return null|string
|
1813 |
-
*/
|
1814 |
-
protected function o_image($id, $action, $options = '')
|
1815 |
-
{
|
1816 |
-
switch ($action) {
|
1817 |
-
case 'new':
|
1818 |
-
// make the new object
|
1819 |
-
$this->objects[$id] = array('t' => 'image', 'data' => &$options['data'], 'info' => array());
|
1820 |
-
|
1821 |
-
$info =& $this->objects[$id]['info'];
|
1822 |
-
|
1823 |
-
$info['Type'] = '/XObject';
|
1824 |
-
$info['Subtype'] = '/Image';
|
1825 |
-
$info['Width'] = $options['iw'];
|
1826 |
-
$info['Height'] = $options['ih'];
|
1827 |
-
|
1828 |
-
if (isset($options['masked']) && $options['masked']) {
|
1829 |
-
$info['SMask'] = ($this->numObj - 1) . ' 0 R';
|
1830 |
-
}
|
1831 |
-
|
1832 |
-
if (!isset($options['type']) || $options['type'] === 'jpg') {
|
1833 |
-
if (!isset($options['channels'])) {
|
1834 |
-
$options['channels'] = 3;
|
1835 |
-
}
|
1836 |
-
|
1837 |
-
switch ($options['channels']) {
|
1838 |
-
case 1:
|
1839 |
-
$info['ColorSpace'] = '/DeviceGray';
|
1840 |
-
break;
|
1841 |
-
case 4:
|
1842 |
-
$info['ColorSpace'] = '/DeviceCMYK';
|
1843 |
-
break;
|
1844 |
-
default:
|
1845 |
-
$info['ColorSpace'] = '/DeviceRGB';
|
1846 |
-
break;
|
1847 |
-
}
|
1848 |
-
|
1849 |
-
if ($info['ColorSpace'] === '/DeviceCMYK') {
|
1850 |
-
$info['Decode'] = '[1 0 1 0 1 0 1 0]';
|
1851 |
-
}
|
1852 |
-
|
1853 |
-
$info['Filter'] = '/DCTDecode';
|
1854 |
-
$info['BitsPerComponent'] = 8;
|
1855 |
-
} else {
|
1856 |
-
if ($options['type'] === 'png') {
|
1857 |
-
$info['Filter'] = '/FlateDecode';
|
1858 |
-
$info['DecodeParms'] = '<< /Predictor 15 /Colors ' . $options['ncolor'] . ' /Columns ' . $options['iw'] . ' /BitsPerComponent ' . $options['bitsPerComponent'] . '>>';
|
1859 |
-
|
1860 |
-
if ($options['isMask']) {
|
1861 |
-
$info['ColorSpace'] = '/DeviceGray';
|
1862 |
-
} else {
|
1863 |
-
if (mb_strlen($options['pdata'], '8bit')) {
|
1864 |
-
$tmp = ' [ /Indexed /DeviceRGB ' . (mb_strlen($options['pdata'], '8bit') / 3 - 1) . ' ';
|
1865 |
-
$this->numObj++;
|
1866 |
-
$this->o_contents($this->numObj, 'new');
|
1867 |
-
$this->objects[$this->numObj]['c'] = $options['pdata'];
|
1868 |
-
$tmp .= $this->numObj . ' 0 R';
|
1869 |
-
$tmp .= ' ]';
|
1870 |
-
$info['ColorSpace'] = $tmp;
|
1871 |
-
|
1872 |
-
if (isset($options['transparency'])) {
|
1873 |
-
$transparency = $options['transparency'];
|
1874 |
-
switch ($transparency['type']) {
|
1875 |
-
case 'indexed':
|
1876 |
-
$tmp = ' [ ' . $transparency['data'] . ' ' . $transparency['data'] . '] ';
|
1877 |
-
$info['Mask'] = $tmp;
|
1878 |
-
break;
|
1879 |
-
|
1880 |
-
case 'color-key':
|
1881 |
-
$tmp = ' [ ' .
|
1882 |
-
$transparency['r'] . ' ' . $transparency['r'] .
|
1883 |
-
$transparency['g'] . ' ' . $transparency['g'] .
|
1884 |
-
$transparency['b'] . ' ' . $transparency['b'] .
|
1885 |
-
' ] ';
|
1886 |
-
$info['Mask'] = $tmp;
|
1887 |
-
break;
|
1888 |
-
}
|
1889 |
-
}
|
1890 |
-
} else {
|
1891 |
-
if (isset($options['transparency'])) {
|
1892 |
-
$transparency = $options['transparency'];
|
1893 |
-
|
1894 |
-
switch ($transparency['type']) {
|
1895 |
-
case 'indexed':
|
1896 |
-
$tmp = ' [ ' . $transparency['data'] . ' ' . $transparency['data'] . '] ';
|
1897 |
-
$info['Mask'] = $tmp;
|
1898 |
-
break;
|
1899 |
-
|
1900 |
-
case 'color-key':
|
1901 |
-
$tmp = ' [ ' .
|
1902 |
-
$transparency['r'] . ' ' . $transparency['r'] . ' ' .
|
1903 |
-
$transparency['g'] . ' ' . $transparency['g'] . ' ' .
|
1904 |
-
$transparency['b'] . ' ' . $transparency['b'] .
|
1905 |
-
' ] ';
|
1906 |
-
$info['Mask'] = $tmp;
|
1907 |
-
break;
|
1908 |
-
}
|
1909 |
-
}
|
1910 |
-
$info['ColorSpace'] = '/' . $options['color'];
|
1911 |
-
}
|
1912 |
-
}
|
1913 |
-
|
1914 |
-
$info['BitsPerComponent'] = $options['bitsPerComponent'];
|
1915 |
-
}
|
1916 |
-
}
|
1917 |
-
|
1918 |
-
// assign it a place in the named resource dictionary as an external object, according to
|
1919 |
-
// the label passed in with it.
|
1920 |
-
$this->o_pages($this->currentNode, 'xObject', array('label' => $options['label'], 'objNum' => $id));
|
1921 |
-
|
1922 |
-
// also make sure that we have the right procset object for it.
|
1923 |
-
$this->o_procset($this->procsetObjectId, 'add', 'ImageC');
|
1924 |
-
break;
|
1925 |
-
|
1926 |
-
case 'out':
|
1927 |
-
$o = &$this->objects[$id];
|
1928 |
-
$tmp = &$o['data'];
|
1929 |
-
$res = "\n$id 0 obj\n<<";
|
1930 |
-
|
1931 |
-
foreach ($o['info'] as $k => $v) {
|
1932 |
-
$res .= "\n/$k $v";
|
1933 |
-
}
|
1934 |
-
|
1935 |
-
if ($this->encrypted) {
|
1936 |
-
$this->encryptInit($id);
|
1937 |
-
$tmp = $this->ARC4($tmp);
|
1938 |
-
}
|
1939 |
-
|
1940 |
-
$res .= "\n/Length " . mb_strlen($tmp, '8bit') . ">>\nstream\n$tmp\nendstream\nendobj";
|
1941 |
-
|
1942 |
-
return $res;
|
1943 |
-
}
|
1944 |
-
|
1945 |
-
return null;
|
1946 |
-
}
|
1947 |
-
|
1948 |
-
/**
|
1949 |
-
* graphics state object
|
1950 |
-
*
|
1951 |
-
* @param $id
|
1952 |
-
* @param $action
|
1953 |
-
* @param string $options
|
1954 |
-
* @return null|string
|
1955 |
-
*/
|
1956 |
-
protected function o_extGState($id, $action, $options = "")
|
1957 |
-
{
|
1958 |
-
static $valid_params = array(
|
1959 |
-
"LW",
|
1960 |
-
"LC",
|
1961 |
-
"LC",
|
1962 |
-
"LJ",
|
1963 |
-
"ML",
|
1964 |
-
"D",
|
1965 |
-
"RI",
|
1966 |
-
"OP",
|
1967 |
-
"op",
|
1968 |
-
"OPM",
|
1969 |
-
"Font",
|
1970 |
-
"BG",
|
1971 |
-
"BG2",
|
1972 |
-
"UCR",
|
1973 |
-
"TR",
|
1974 |
-
"TR2",
|
1975 |
-
"HT",
|
1976 |
-
"FL",
|
1977 |
-
"SM",
|
1978 |
-
"SA",
|
1979 |
-
"BM",
|
1980 |
-
"SMask",
|
1981 |
-
"CA",
|
1982 |
-
"ca",
|
1983 |
-
"AIS",
|
1984 |
-
"TK"
|
1985 |
-
);
|
1986 |
-
|
1987 |
-
switch ($action) {
|
1988 |
-
case "new":
|
1989 |
-
$this->objects[$id] = array('t' => 'extGState', 'info' => $options);
|
1990 |
-
|
1991 |
-
// Tell the pages about the new resource
|
1992 |
-
$this->numStates++;
|
1993 |
-
$this->o_pages($this->currentNode, 'extGState', array("objNum" => $id, "stateNum" => $this->numStates));
|
1994 |
-
break;
|
1995 |
-
|
1996 |
-
case "out":
|
1997 |
-
$o = &$this->objects[$id];
|
1998 |
-
$res = "\n$id 0 obj\n<< /Type /ExtGState\n";
|
1999 |
-
|
2000 |
-
foreach ($o["info"] as $k => $v) {
|
2001 |
-
if (!in_array($k, $valid_params)) {
|
2002 |
-
continue;
|
2003 |
-
}
|
2004 |
-
$res .= "/$k $v\n";
|
2005 |
-
}
|
2006 |
-
|
2007 |
-
$res .= ">>\nendobj";
|
2008 |
-
|
2009 |
-
return $res;
|
2010 |
-
}
|
2011 |
-
|
2012 |
-
return null;
|
2013 |
-
}
|
2014 |
-
|
2015 |
-
/**
|
2016 |
-
* encryption object.
|
2017 |
-
*
|
2018 |
-
* @param $id
|
2019 |
-
* @param $action
|
2020 |
-
* @param string $options
|
2021 |
-
* @return string|null
|
2022 |
-
*/
|
2023 |
-
protected function o_encryption($id, $action, $options = '')
|
2024 |
-
{
|
2025 |
-
switch ($action) {
|
2026 |
-
case 'new':
|
2027 |
-
// make the new object
|
2028 |
-
$this->objects[$id] = array('t' => 'encryption', 'info' => $options);
|
2029 |
-
$this->arc4_objnum = $id;
|
2030 |
-
break;
|
2031 |
-
|
2032 |
-
case 'keys':
|
2033 |
-
// figure out the additional parameters required
|
2034 |
-
$pad = chr(0x28) . chr(0xBF) . chr(0x4E) . chr(0x5E) . chr(0x4E) . chr(0x75) . chr(0x8A) . chr(0x41)
|
2035 |
-
. chr(0x64) . chr(0x00) . chr(0x4E) . chr(0x56) . chr(0xFF) . chr(0xFA) . chr(0x01) . chr(0x08)
|
2036 |
-
. chr(0x2E) . chr(0x2E) . chr(0x00) . chr(0xB6) . chr(0xD0) . chr(0x68) . chr(0x3E) . chr(0x80)
|
2037 |
-
. chr(0x2F) . chr(0x0C) . chr(0xA9) . chr(0xFE) . chr(0x64) . chr(0x53) . chr(0x69) . chr(0x7A);
|
2038 |
-
|
2039 |
-
$info = $this->objects[$id]['info'];
|
2040 |
-
|
2041 |
-
$len = mb_strlen($info['owner'], '8bit');
|
2042 |
-
|
2043 |
-
if ($len > 32) {
|
2044 |
-
$owner = substr($info['owner'], 0, 32);
|
2045 |
-
} else {
|
2046 |
-
if ($len < 32) {
|
2047 |
-
$owner = $info['owner'] . substr($pad, 0, 32 - $len);
|
2048 |
-
} else {
|
2049 |
-
$owner = $info['owner'];
|
2050 |
-
}
|
2051 |
-
}
|
2052 |
-
|
2053 |
-
$len = mb_strlen($info['user'], '8bit');
|
2054 |
-
if ($len > 32) {
|
2055 |
-
$user = substr($info['user'], 0, 32);
|
2056 |
-
} else {
|
2057 |
-
if ($len < 32) {
|
2058 |
-
$user = $info['user'] . substr($pad, 0, 32 - $len);
|
2059 |
-
} else {
|
2060 |
-
$user = $info['user'];
|
2061 |
-
}
|
2062 |
-
}
|
2063 |
-
|
2064 |
-
$tmp = $this->md5_16($owner);
|
2065 |
-
$okey = substr($tmp, 0, 5);
|
2066 |
-
$this->ARC4_init($okey);
|
2067 |
-
$ovalue = $this->ARC4($user);
|
2068 |
-
$this->objects[$id]['info']['O'] = $ovalue;
|
2069 |
-
|
2070 |
-
// now make the u value, phew.
|
2071 |
-
$tmp = $this->md5_16(
|
2072 |
-
$user . $ovalue . chr($info['p']) . chr(255) . chr(255) . chr(255) . hex2bin($this->fileIdentifier)
|
2073 |
-
);
|
2074 |
-
|
2075 |
-
$ukey = substr($tmp, 0, 5);
|
2076 |
-
$this->ARC4_init($ukey);
|
2077 |
-
$this->encryptionKey = $ukey;
|
2078 |
-
$this->encrypted = true;
|
2079 |
-
$uvalue = $this->ARC4($pad);
|
2080 |
-
$this->objects[$id]['info']['U'] = $uvalue;
|
2081 |
-
// initialize the arc4 array
|
2082 |
-
break;
|
2083 |
-
|
2084 |
-
case 'out':
|
2085 |
-
$o = &$this->objects[$id];
|
2086 |
-
|
2087 |
-
$res = "\n$id 0 obj\n<<";
|
2088 |
-
$res .= "\n/Filter /Standard";
|
2089 |
-
$res .= "\n/V 1";
|
2090 |
-
$res .= "\n/R 2";
|
2091 |
-
$res .= "\n/O (" . $this->filterText($o['info']['O'], false, false) . ')';
|
2092 |
-
$res .= "\n/U (" . $this->filterText($o['info']['U'], false, false) . ')';
|
2093 |
-
// and the p-value needs to be converted to account for the twos-complement approach
|
2094 |
-
$o['info']['p'] = (($o['info']['p'] ^ 255) + 1) * -1;
|
2095 |
-
$res .= "\n/P " . ($o['info']['p']);
|
2096 |
-
$res .= "\n>>\nendobj";
|
2097 |
-
|
2098 |
-
return $res;
|
2099 |
-
}
|
2100 |
-
|
2101 |
-
return null;
|
2102 |
-
}
|
2103 |
-
|
2104 |
-
/**
|
2105 |
-
* ARC4 functions
|
2106 |
-
* A series of function to implement ARC4 encoding in PHP
|
2107 |
-
*/
|
2108 |
-
|
2109 |
-
/**
|
2110 |
-
* calculate the 16 byte version of the 128 bit md5 digest of the string
|
2111 |
-
*
|
2112 |
-
* @param $string
|
2113 |
-
* @return string
|
2114 |
-
*/
|
2115 |
-
function md5_16($string)
|
2116 |
-
{
|
2117 |
-
$tmp = md5($string);
|
2118 |
-
$out = '';
|
2119 |
-
for ($i = 0; $i <= 30; $i = $i + 2) {
|
2120 |
-
$out .= chr(hexdec(substr($tmp, $i, 2)));
|
2121 |
-
}
|
2122 |
-
|
2123 |
-
return $out;
|
2124 |
-
}
|
2125 |
-
|
2126 |
-
/**
|
2127 |
-
* initialize the encryption for processing a particular object
|
2128 |
-
*
|
2129 |
-
* @param $id
|
2130 |
-
*/
|
2131 |
-
function encryptInit($id)
|
2132 |
-
{
|
2133 |
-
$tmp = $this->encryptionKey;
|
2134 |
-
$hex = dechex($id);
|
2135 |
-
if (mb_strlen($hex, '8bit') < 6) {
|
2136 |
-
$hex = substr('000000', 0, 6 - mb_strlen($hex, '8bit')) . $hex;
|
2137 |
-
}
|
2138 |
-
$tmp .= chr(hexdec(substr($hex, 4, 2)))
|
2139 |
-
. chr(hexdec(substr($hex, 2, 2)))
|
2140 |
-
. chr(hexdec(substr($hex, 0, 2)))
|
2141 |
-
. chr(0)
|
2142 |
-
. chr(0)
|
2143 |
-
;
|
2144 |
-
$key = $this->md5_16($tmp);
|
2145 |
-
$this->ARC4_init(substr($key, 0, 10));
|
2146 |
-
}
|
2147 |
-
|
2148 |
-
/**
|
2149 |
-
* initialize the ARC4 encryption
|
2150 |
-
*
|
2151 |
-
* @param string $key
|
2152 |
-
*/
|
2153 |
-
function ARC4_init($key = '')
|
2154 |
-
{
|
2155 |
-
$this->arc4 = '';
|
2156 |
-
|
2157 |
-
// setup the control array
|
2158 |
-
if (mb_strlen($key, '8bit') == 0) {
|
2159 |
-
return;
|
2160 |
-
}
|
2161 |
-
|
2162 |
-
$k = '';
|
2163 |
-
while (mb_strlen($k, '8bit') < 256) {
|
2164 |
-
$k .= $key;
|
2165 |
-
}
|
2166 |
-
|
2167 |
-
$k = substr($k, 0, 256);
|
2168 |
-
for ($i = 0; $i < 256; $i++) {
|
2169 |
-
$this->arc4 .= chr($i);
|
2170 |
-
}
|
2171 |
-
|
2172 |
-
$j = 0;
|
2173 |
-
|
2174 |
-
for ($i = 0; $i < 256; $i++) {
|
2175 |
-
$t = $this->arc4[$i];
|
2176 |
-
$j = ($j + ord($t) + ord($k[$i])) % 256;
|
2177 |
-
$this->arc4[$i] = $this->arc4[$j];
|
2178 |
-
$this->arc4[$j] = $t;
|
2179 |
-
}
|
2180 |
-
}
|
2181 |
-
|
2182 |
-
/**
|
2183 |
-
* ARC4 encrypt a text string
|
2184 |
-
*
|
2185 |
-
* @param $text
|
2186 |
-
* @return string
|
2187 |
-
*/
|
2188 |
-
function ARC4($text)
|
2189 |
-
{
|
2190 |
-
$len = mb_strlen($text, '8bit');
|
2191 |
-
$a = 0;
|
2192 |
-
$b = 0;
|
2193 |
-
$c = $this->arc4;
|
2194 |
-
$out = '';
|
2195 |
-
for ($i = 0; $i < $len; $i++) {
|
2196 |
-
$a = ($a + 1) % 256;
|
2197 |
-
$t = $c[$a];
|
2198 |
-
$b = ($b + ord($t)) % 256;
|
2199 |
-
$c[$a] = $c[$b];
|
2200 |
-
$c[$b] = $t;
|
2201 |
-
$k = ord($c[(ord($c[$a]) + ord($c[$b])) % 256]);
|
2202 |
-
$out .= chr(ord($text[$i]) ^ $k);
|
2203 |
-
}
|
2204 |
-
|
2205 |
-
return $out;
|
2206 |
-
}
|
2207 |
-
|
2208 |
-
/**
|
2209 |
-
* functions which can be called to adjust or add to the document
|
2210 |
-
*/
|
2211 |
-
|
2212 |
-
/**
|
2213 |
-
* add a link in the document to an external URL
|
2214 |
-
*
|
2215 |
-
* @param $url
|
2216 |
-
* @param $x0
|
2217 |
-
* @param $y0
|
2218 |
-
* @param $x1
|
2219 |
-
* @param $y1
|
2220 |
-
*/
|
2221 |
-
function addLink($url, $x0, $y0, $x1, $y1)
|
2222 |
-
{
|
2223 |
-
$this->numObj++;
|
2224 |
-
$info = array('type' => 'link', 'url' => $url, 'rect' => array($x0, $y0, $x1, $y1));
|
2225 |
-
$this->o_annotation($this->numObj, 'new', $info);
|
2226 |
-
}
|
2227 |
-
|
2228 |
-
/**
|
2229 |
-
* add a link in the document to an internal destination (ie. within the document)
|
2230 |
-
*
|
2231 |
-
* @param $label
|
2232 |
-
* @param $x0
|
2233 |
-
* @param $y0
|
2234 |
-
* @param $x1
|
2235 |
-
* @param $y1
|
2236 |
-
*/
|
2237 |
-
function addInternalLink($label, $x0, $y0, $x1, $y1)
|
2238 |
-
{
|
2239 |
-
$this->numObj++;
|
2240 |
-
$info = array('type' => 'ilink', 'label' => $label, 'rect' => array($x0, $y0, $x1, $y1));
|
2241 |
-
$this->o_annotation($this->numObj, 'new', $info);
|
2242 |
-
}
|
2243 |
-
|
2244 |
-
/**
|
2245 |
-
* set the encryption of the document
|
2246 |
-
* can be used to turn it on and/or set the passwords which it will have.
|
2247 |
-
* also the functions that the user will have are set here, such as print, modify, add
|
2248 |
-
*
|
2249 |
-
* @param string $userPass
|
2250 |
-
* @param string $ownerPass
|
2251 |
-
* @param array $pc
|
2252 |
-
*/
|
2253 |
-
function setEncryption($userPass = '', $ownerPass = '', $pc = array())
|
2254 |
-
{
|
2255 |
-
$p = bindec("11000000");
|
2256 |
-
|
2257 |
-
$options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'add' => 32);
|
2258 |
-
|
2259 |
-
foreach ($pc as $k => $v) {
|
2260 |
-
if ($v && isset($options[$k])) {
|
2261 |
-
$p += $options[$k];
|
2262 |
-
} else {
|
2263 |
-
if (isset($options[$v])) {
|
2264 |
-
$p += $options[$v];
|
2265 |
-
}
|
2266 |
-
}
|
2267 |
-
}
|
2268 |
-
|
2269 |
-
// implement encryption on the document
|
2270 |
-
if ($this->arc4_objnum == 0) {
|
2271 |
-
// then the block does not exist already, add it.
|
2272 |
-
$this->numObj++;
|
2273 |
-
if (mb_strlen($ownerPass) == 0) {
|
2274 |
-
$ownerPass = $userPass;
|
2275 |
-
}
|
2276 |
-
|
2277 |
-
$this->o_encryption($this->numObj, 'new', array('user' => $userPass, 'owner' => $ownerPass, 'p' => $p));
|
2278 |
-
}
|
2279 |
-
}
|
2280 |
-
|
2281 |
-
/**
|
2282 |
-
* should be used for internal checks, not implemented as yet
|
2283 |
-
*/
|
2284 |
-
function checkAllHere()
|
2285 |
-
{
|
2286 |
-
}
|
2287 |
-
|
2288 |
-
/**
|
2289 |
-
* return the pdf stream as a string returned from the function
|
2290 |
-
*
|
2291 |
-
* @param bool $debug
|
2292 |
-
* @return string
|
2293 |
-
*/
|
2294 |
-
function output($debug = false)
|
2295 |
-
{
|
2296 |
-
if ($debug) {
|
2297 |
-
// turn compression off
|
2298 |
-
$this->options['compression'] = false;
|
2299 |
-
}
|
2300 |
-
|
2301 |
-
if ($this->javascript) {
|
2302 |
-
$this->numObj++;
|
2303 |
-
|
2304 |
-
$js_id = $this->numObj;
|
2305 |
-
$this->o_embedjs($js_id, 'new');
|
2306 |
-
$this->o_javascript(++$this->numObj, 'new', $this->javascript);
|
2307 |
-
|
2308 |
-
$id = $this->catalogId;
|
2309 |
-
|
2310 |
-
$this->o_catalog($id, 'javascript', $js_id);
|
2311 |
-
}
|
2312 |
-
|
2313 |
-
if ($this->fileIdentifier === '') {
|
2314 |
-
$tmp = implode('', $this->objects[$this->infoObject]['info']);
|
2315 |
-
$this->fileIdentifier = md5('DOMPDF' . __FILE__ . $tmp . microtime() . mt_rand());
|
2316 |
-
}
|
2317 |
-
|
2318 |
-
if ($this->arc4_objnum) {
|
2319 |
-
$this->o_encryption($this->arc4_objnum, 'keys');
|
2320 |
-
$this->ARC4_init($this->encryptionKey);
|
2321 |
-
}
|
2322 |
-
|
2323 |
-
$this->checkAllHere();
|
2324 |
-
|
2325 |
-
$xref = array();
|
2326 |
-
$content = '%PDF-1.3';
|
2327 |
-
$pos = mb_strlen($content, '8bit');
|
2328 |
-
|
2329 |
-
foreach ($this->objects as $k => $v) {
|
2330 |
-
$tmp = 'o_' . $v['t'];
|
2331 |
-
$cont = $this->$tmp($k, 'out');
|
2332 |
-
$content .= $cont;
|
2333 |
-
$xref[] = $pos + 1; //+1 to account for \n at the start of each object
|
2334 |
-
$pos += mb_strlen($cont, '8bit');
|
2335 |
-
}
|
2336 |
-
|
2337 |
-
$content .= "\nxref\n0 " . (count($xref) + 1) . "\n0000000000 65535 f \n";
|
2338 |
-
|
2339 |
-
foreach ($xref as $p) {
|
2340 |
-
$content .= str_pad($p, 10, "0", STR_PAD_LEFT) . " 00000 n \n";
|
2341 |
-
}
|
2342 |
-
|
2343 |
-
$content .= "trailer\n<<\n" .
|
2344 |
-
'/Size ' . (count($xref) + 1) . "\n" .
|
2345 |
-
'/Root 1 0 R' . "\n" .
|
2346 |
-
'/Info ' . $this->infoObject . " 0 R\n"
|
2347 |
-
;
|
2348 |
-
|
2349 |
-
// if encryption has been applied to this document then add the marker for this dictionary
|
2350 |
-
if ($this->arc4_objnum > 0) {
|
2351 |
-
$content .= '/Encrypt ' . $this->arc4_objnum . " 0 R\n";
|
2352 |
-
}
|
2353 |
-
|
2354 |
-
$content .= '/ID[<' . $this->fileIdentifier . '><' . $this->fileIdentifier . ">]\n";
|
2355 |
-
|
2356 |
-
// account for \n added at start of xref table
|
2357 |
-
$pos++;
|
2358 |
-
|
2359 |
-
$content .= ">>\nstartxref\n$pos\n%%EOF\n";
|
2360 |
-
|
2361 |
-
return $content;
|
2362 |
-
}
|
2363 |
-
|
2364 |
-
/**
|
2365 |
-
* initialize a new document
|
2366 |
-
* if this is called on an existing document results may be unpredictable, but the existing document would be lost at minimum
|
2367 |
-
* this function is called automatically by the constructor function
|
2368 |
-
*
|
2369 |
-
* @param array $pageSize
|
2370 |
-
*/
|
2371 |
-
private function newDocument($pageSize = array(0, 0, 612, 792))
|
2372 |
-
{
|
2373 |
-
$this->numObj = 0;
|
2374 |
-
$this->objects = array();
|
2375 |
-
|
2376 |
-
$this->numObj++;
|
2377 |
-
$this->o_catalog($this->numObj, 'new');
|
2378 |
-
|
2379 |
-
$this->numObj++;
|
2380 |
-
$this->o_outlines($this->numObj, 'new');
|
2381 |
-
|
2382 |
-
$this->numObj++;
|
2383 |
-
$this->o_pages($this->numObj, 'new');
|
2384 |
-
|
2385 |
-
$this->o_pages($this->numObj, 'mediaBox', $pageSize);
|
2386 |
-
$this->currentNode = 3;
|
2387 |
-
|
2388 |
-
$this->numObj++;
|
2389 |
-
$this->o_procset($this->numObj, 'new');
|
2390 |
-
|
2391 |
-
$this->numObj++;
|
2392 |
-
$this->o_info($this->numObj, 'new');
|
2393 |
-
|
2394 |
-
$this->numObj++;
|
2395 |
-
$this->o_page($this->numObj, 'new');
|
2396 |
-
|
2397 |
-
// need to store the first page id as there is no way to get it to the user during
|
2398 |
-
// startup
|
2399 |
-
$this->firstPageId = $this->currentContents;
|
2400 |
-
}
|
2401 |
-
|
2402 |
-
/**
|
2403 |
-
* open the font file and return a php structure containing it.
|
2404 |
-
* first check if this one has been done before and saved in a form more suited to php
|
2405 |
-
* note that if a php serialized version does not exist it will try and make one, but will
|
2406 |
-
* require write access to the directory to do it... it is MUCH faster to have these serialized
|
2407 |
-
* files.
|
2408 |
-
*
|
2409 |
-
* @param $font
|
2410 |
-
*/
|
2411 |
-
private function openFont($font)
|
2412 |
-
{
|
2413 |
-
// assume that $font contains the path and file but not the extension
|
2414 |
-
$name = basename($font);
|
2415 |
-
$dir = dirname($font) . '/';
|
2416 |
-
|
2417 |
-
$fontcache = $this->fontcache;
|
2418 |
-
if ($fontcache == '') {
|
2419 |
-
$fontcache = rtrim($dir, DIRECTORY_SEPARATOR."/\\");
|
2420 |
-
}
|
2421 |
-
|
2422 |
-
//$name filename without folder and extension of font metrics
|
2423 |
-
//$dir folder of font metrics
|
2424 |
-
//$fontcache folder of runtime created php serialized version of font metrics.
|
2425 |
-
// If this is not given, the same folder as the font metrics will be used.
|
2426 |
-
// Storing and reusing serialized versions improves speed much
|
2427 |
-
|
2428 |
-
$this->addMessage("openFont: $font - $name");
|
2429 |
-
|
2430 |
-
if (!$this->isUnicode || in_array(mb_strtolower(basename($name)), self::$coreFonts)) {
|
2431 |
-
$metrics_name = "$name.afm";
|
2432 |
-
} else {
|
2433 |
-
$metrics_name = "$name.ufm";
|
2434 |
-
}
|
2435 |
-
|
2436 |
-
$cache_name = "$metrics_name.php";
|
2437 |
-
$this->addMessage("metrics: $metrics_name, cache: $cache_name");
|
2438 |
-
|
2439 |
-
if (file_exists($fontcache . '/' . $cache_name)) {
|
2440 |
-
$this->addMessage("openFont: php file exists $fontcache/$cache_name");
|
2441 |
-
$this->fonts[$font] = require($fontcache . '/' . $cache_name);
|
2442 |
-
|
2443 |
-
if (!isset($this->fonts[$font]['_version_']) || $this->fonts[$font]['_version_'] != $this->fontcacheVersion) {
|
2444 |
-
// if the font file is old, then clear it out and prepare for re-creation
|
2445 |
-
$this->addMessage('openFont: clear out, make way for new version.');
|
2446 |
-
$this->fonts[$font] = null;
|
2447 |
-
unset($this->fonts[$font]);
|
2448 |
-
}
|
2449 |
-
} else {
|
2450 |
-
$old_cache_name = "php_$metrics_name";
|
2451 |
-
if (file_exists($fontcache . '/' . $old_cache_name)) {
|
2452 |
-
$this->addMessage(
|
2453 |
-
"openFont: php file doesn't exist $fontcache/$cache_name, creating it from the old format"
|
2454 |
-
);
|
2455 |
-
$old_cache = file_get_contents($fontcache . '/' . $old_cache_name);
|
2456 |
-
file_put_contents($fontcache . '/' . $cache_name, '<?php return ' . $old_cache . ';');
|
2457 |
-
|
2458 |
-
$this->openFont($font);
|
2459 |
-
return;
|
2460 |
-
}
|
2461 |
-
}
|
2462 |
-
|
2463 |
-
if (!isset($this->fonts[$font]) && file_exists($dir . $metrics_name)) {
|
2464 |
-
// then rebuild the php_<font>.afm file from the <font>.afm file
|
2465 |
-
$this->addMessage("openFont: build php file from $dir$metrics_name");
|
2466 |
-
$data = array();
|
2467 |
-
|
2468 |
-
// 20 => 'space'
|
2469 |
-
$data['codeToName'] = array();
|
2470 |
-
|
2471 |
-
// Since we're not going to enable Unicode for the core fonts we need to use a font-based
|
2472 |
-
// setting for Unicode support rather than a global setting.
|
2473 |
-
$data['isUnicode'] = (strtolower(substr($metrics_name, -3)) !== 'afm');
|
2474 |
-
|
2475 |
-
$cidtogid = '';
|
2476 |
-
if ($data['isUnicode']) {
|
2477 |
-
$cidtogid = str_pad('', 256 * 256 * 2, "\x00");
|
2478 |
-
}
|
2479 |
-
|
2480 |
-
$file = file($dir . $metrics_name);
|
2481 |
-
|
2482 |
-
foreach ($file as $rowA) {
|
2483 |
-
$row = trim($rowA);
|
2484 |
-
$pos = strpos($row, ' ');
|
2485 |
-
|
2486 |
-
if ($pos) {
|
2487 |
-
// then there must be some keyword
|
2488 |
-
$key = substr($row, 0, $pos);
|
2489 |
-
switch ($key) {
|
2490 |
-
case 'FontName':
|
2491 |
-
case 'FullName':
|
2492 |
-
case 'FamilyName':
|
2493 |
-
case 'PostScriptName':
|
2494 |
-
case 'Weight':
|
2495 |
-
case 'ItalicAngle':
|
2496 |
-
case 'IsFixedPitch':
|
2497 |
-
case 'CharacterSet':
|
2498 |
-
case 'UnderlinePosition':
|
2499 |
-
case 'UnderlineThickness':
|
2500 |
-
case 'Version':
|
2501 |
-
case 'EncodingScheme':
|
2502 |
-
case 'CapHeight':
|
2503 |
-
case 'XHeight':
|
2504 |
-
case 'Ascender':
|
2505 |
-
case 'Descender':
|
2506 |
-
case 'StdHW':
|
2507 |
-
case 'StdVW':
|
2508 |
-
case 'StartCharMetrics':
|
2509 |
-
case 'FontHeightOffset': // OAR - Added so we can offset the height calculation of a Windows font. Otherwise it's too big.
|
2510 |
-
$data[$key] = trim(substr($row, $pos));
|
2511 |
-
break;
|
2512 |
-
|
2513 |
-
case 'FontBBox':
|
2514 |
-
$data[$key] = explode(' ', trim(substr($row, $pos)));
|
2515 |
-
break;
|
2516 |
-
|
2517 |
-
//C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ;
|
2518 |
-
case 'C': // Found in AFM files
|
2519 |
-
$bits = explode(';', trim($row));
|
2520 |
-
$dtmp = array();
|
2521 |
-
|
2522 |
-
foreach ($bits as $bit) {
|
2523 |
-
$bits2 = explode(' ', trim($bit));
|
2524 |
-
if (mb_strlen($bits2[0], '8bit') == 0) {
|
2525 |
-
continue;
|
2526 |
-
}
|
2527 |
-
|
2528 |
-
if (count($bits2) > 2) {
|
2529 |
-
$dtmp[$bits2[0]] = array();
|
2530 |
-
for ($i = 1; $i < count($bits2); $i++) {
|
2531 |
-
$dtmp[$bits2[0]][] = $bits2[$i];
|
2532 |
-
}
|
2533 |
-
} else {
|
2534 |
-
if (count($bits2) == 2) {
|
2535 |
-
$dtmp[$bits2[0]] = $bits2[1];
|
2536 |
-
}
|
2537 |
-
}
|
2538 |
-
}
|
2539 |
-
|
2540 |
-
$c = (int)$dtmp['C'];
|
2541 |
-
$n = $dtmp['N'];
|
2542 |
-
$width = floatval($dtmp['WX']);
|
2543 |
-
|
2544 |
-
if ($c >= 0) {
|
2545 |
-
if ($c != hexdec($n)) {
|
2546 |
-
$data['codeToName'][$c] = $n;
|
2547 |
-
}
|
2548 |
-
$data['C'][$c] = $width;
|
2549 |
-
} else {
|
2550 |
-
$data['C'][$n] = $width;
|
2551 |
-
}
|
2552 |
-
|
2553 |
-
if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
|
2554 |
-
$data['MissingWidth'] = $width;
|
2555 |
-
}
|
2556 |
-
|
2557 |
-
break;
|
2558 |
-
|
2559 |
-
// U 827 ; WX 0 ; N squaresubnosp ; G 675 ;
|
2560 |
-
case 'U': // Found in UFM files
|
2561 |
-
if (!$data['isUnicode']) {
|
2562 |
-
break;
|
2563 |
-
}
|
2564 |
-
|
2565 |
-
$bits = explode(';', trim($row));
|
2566 |
-
$dtmp = array();
|
2567 |
-
|
2568 |
-
foreach ($bits as $bit) {
|
2569 |
-
$bits2 = explode(' ', trim($bit));
|
2570 |
-
if (mb_strlen($bits2[0], '8bit') === 0) {
|
2571 |
-
continue;
|
2572 |
-
}
|
2573 |
-
|
2574 |
-
if (count($bits2) > 2) {
|
2575 |
-
$dtmp[$bits2[0]] = array();
|
2576 |
-
for ($i = 1; $i < count($bits2); $i++) {
|
2577 |
-
$dtmp[$bits2[0]][] = $bits2[$i];
|
2578 |
-
}
|
2579 |
-
} else {
|
2580 |
-
if (count($bits2) == 2) {
|
2581 |
-
$dtmp[$bits2[0]] = $bits2[1];
|
2582 |
-
}
|
2583 |
-
}
|
2584 |
-
}
|
2585 |
-
|
2586 |
-
$c = (int)$dtmp['U'];
|
2587 |
-
$n = $dtmp['N'];
|
2588 |
-
$glyph = $dtmp['G'];
|
2589 |
-
$width = floatval($dtmp['WX']);
|
2590 |
-
|
2591 |
-
if ($c >= 0) {
|
2592 |
-
// Set values in CID to GID map
|
2593 |
-
if ($c >= 0 && $c < 0xFFFF && $glyph) {
|
2594 |
-
$cidtogid[$c * 2] = chr($glyph >> 8);
|
2595 |
-
$cidtogid[$c * 2 + 1] = chr($glyph & 0xFF);
|
2596 |
-
}
|
2597 |
-
|
2598 |
-
if ($c != hexdec($n)) {
|
2599 |
-
$data['codeToName'][$c] = $n;
|
2600 |
-
}
|
2601 |
-
$data['C'][$c] = $width;
|
2602 |
-
} else {
|
2603 |
-
$data['C'][$n] = $width;
|
2604 |
-
}
|
2605 |
-
|
2606 |
-
if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
|
2607 |
-
$data['MissingWidth'] = $width;
|
2608 |
-
}
|
2609 |
-
|
2610 |
-
break;
|
2611 |
-
|
2612 |
-
case 'KPX':
|
2613 |
-
break; // don't include them as they are not used yet
|
2614 |
-
//KPX Adieresis yacute -40
|
2615 |
-
/*$bits = explode(' ', trim($row));
|
2616 |
-
$data['KPX'][$bits[1]][$bits[2]] = $bits[3];
|
2617 |
-
break;*/
|
2618 |
-
}
|
2619 |
-
}
|
2620 |
-
}
|
2621 |
-
|
2622 |
-
if ($this->compressionReady && $this->options['compression']) {
|
2623 |
-
// then implement ZLIB based compression on CIDtoGID string
|
2624 |
-
$data['CIDtoGID_Compressed'] = true;
|
2625 |
-
$cidtogid = gzcompress($cidtogid, 6);
|
2626 |
-
}
|
2627 |
-
$data['CIDtoGID'] = base64_encode($cidtogid);
|
2628 |
-
$data['_version_'] = $this->fontcacheVersion;
|
2629 |
-
$this->fonts[$font] = $data;
|
2630 |
-
|
2631 |
-
//Because of potential trouble with php safe mode, expect that the folder already exists.
|
2632 |
-
//If not existing, this will hit performance because of missing cached results.
|
2633 |
-
if (is_dir($fontcache) && is_writable($fontcache)) {
|
2634 |
-
file_put_contents($fontcache . '/' . $cache_name, '<?php return ' . var_export($data, true) . ';');
|
2635 |
-
}
|
2636 |
-
$data = null;
|
2637 |
-
}
|
2638 |
-
|
2639 |
-
if (!isset($this->fonts[$font])) {
|
2640 |
-
$this->addMessage("openFont: no font file found for $font. Do you need to run load_font.php?");
|
2641 |
-
}
|
2642 |
-
|
2643 |
-
//pre_r($this->messages);
|
2644 |
-
}
|
2645 |
-
|
2646 |
-
/**
|
2647 |
-
* if the font is not loaded then load it and make the required object
|
2648 |
-
* else just make it the current font
|
2649 |
-
* the encoding array can contain 'encoding'=> 'none','WinAnsiEncoding','MacRomanEncoding' or 'MacExpertEncoding'
|
2650 |
-
* note that encoding='none' will need to be used for symbolic fonts
|
2651 |
-
* and 'differences' => an array of mappings between numbers 0->255 and character names.
|
2652 |
-
*
|
2653 |
-
* @param $fontName
|
2654 |
-
* @param string $encoding
|
2655 |
-
* @param bool $set
|
2656 |
-
* @return int
|
2657 |
-
*/
|
2658 |
-
function selectFont($fontName, $encoding = '', $set = true)
|
2659 |
-
{
|
2660 |
-
$ext = substr($fontName, -4);
|
2661 |
-
if ($ext === '.afm' || $ext === '.ufm') {
|
2662 |
-
$fontName = substr($fontName, 0, mb_strlen($fontName) - 4);
|
2663 |
-
}
|
2664 |
-
|
2665 |
-
if (!isset($this->fonts[$fontName])) {
|
2666 |
-
$this->addMessage("selectFont: selecting - $fontName - $encoding, $set");
|
2667 |
-
|
2668 |
-
// load the file
|
2669 |
-
$this->openFont($fontName);
|
2670 |
-
|
2671 |
-
if (isset($this->fonts[$fontName])) {
|
2672 |
-
$this->numObj++;
|
2673 |
-
$this->numFonts++;
|
2674 |
-
|
2675 |
-
$font = &$this->fonts[$fontName];
|
2676 |
-
|
2677 |
-
$name = basename($fontName);
|
2678 |
-
$dir = dirname($fontName) . '/';
|
2679 |
-
$options = array('name' => $name, 'fontFileName' => $fontName);
|
2680 |
-
|
2681 |
-
if (is_array($encoding)) {
|
2682 |
-
// then encoding and differences might be set
|
2683 |
-
if (isset($encoding['encoding'])) {
|
2684 |
-
$options['encoding'] = $encoding['encoding'];
|
2685 |
-
}
|
2686 |
-
|
2687 |
-
if (isset($encoding['differences'])) {
|
2688 |
-
$options['differences'] = $encoding['differences'];
|
2689 |
-
}
|
2690 |
-
} else {
|
2691 |
-
if (mb_strlen($encoding, '8bit')) {
|
2692 |
-
// then perhaps only the encoding has been set
|
2693 |
-
$options['encoding'] = $encoding;
|
2694 |
-
}
|
2695 |
-
}
|
2696 |
-
|
2697 |
-
$fontObj = $this->numObj;
|
2698 |
-
$this->o_font($this->numObj, 'new', $options);
|
2699 |
-
$font['fontNum'] = $this->numFonts;
|
2700 |
-
|
2701 |
-
// if this is a '.afm' font, and there is a '.pfa' file to go with it (as there
|
2702 |
-
// should be for all non-basic fonts), then load it into an object and put the
|
2703 |
-
// references into the font object
|
2704 |
-
$basefile = $fontName;
|
2705 |
-
|
2706 |
-
$fbtype = '';
|
2707 |
-
if (file_exists("$basefile.ttf")) {
|
2708 |
-
$fbtype = 'ttf';
|
2709 |
-
} elseif (file_exists("$basefile.TTF")) {
|
2710 |
-
$fbtype = 'TTF';
|
2711 |
-
} elseif (file_exists("$basefile.pfb")) {
|
2712 |
-
$fbtype = 'pfb';
|
2713 |
-
} elseif (file_exists("$basefile.PFB")) {
|
2714 |
-
$fbtype = 'PFB';
|
2715 |
-
}
|
2716 |
-
|
2717 |
-
$fbfile = "$basefile.$fbtype";
|
2718 |
-
|
2719 |
-
// $pfbfile = substr($fontName,0,strlen($fontName)-4).'.pfb';
|
2720 |
-
// $ttffile = substr($fontName,0,strlen($fontName)-4).'.ttf';
|
2721 |
-
$this->addMessage('selectFont: checking for - ' . $fbfile);
|
2722 |
-
|
2723 |
-
// OAR - I don't understand this old check
|
2724 |
-
// if (substr($fontName, -4) === '.afm' && strlen($fbtype)) {
|
2725 |
-
if ($fbtype) {
|
2726 |
-
$adobeFontName = isset($font['PostScriptName']) ? $font['PostScriptName'] : $font['FontName'];
|
2727 |
-
// $fontObj = $this->numObj;
|
2728 |
-
$this->addMessage("selectFont: adding font file - $fbfile - $adobeFontName");
|
2729 |
-
|
2730 |
-
// find the array of font widths, and put that into an object.
|
2731 |
-
$firstChar = -1;
|
2732 |
-
$lastChar = 0;
|
2733 |
-
$widths = array();
|
2734 |
-
$cid_widths = array();
|
2735 |
-
|
2736 |
-
foreach ($font['C'] as $num => $d) {
|
2737 |
-
if (intval($num) > 0 || $num == '0') {
|
2738 |
-
if (!$font['isUnicode']) {
|
2739 |
-
// With Unicode, widths array isn't used
|
2740 |
-
if ($lastChar > 0 && $num > $lastChar + 1) {
|
2741 |
-
for ($i = $lastChar + 1; $i < $num; $i++) {
|
2742 |
-
$widths[] = 0;
|
2743 |
-
}
|
2744 |
-
}
|
2745 |
-
}
|
2746 |
-
|
2747 |
-
$widths[] = $d;
|
2748 |
-
|
2749 |
-
if ($font['isUnicode']) {
|
2750 |
-
$cid_widths[$num] = $d;
|
2751 |
-
}
|
2752 |
-
|
2753 |
-
if ($firstChar == -1) {
|
2754 |
-
$firstChar = $num;
|
2755 |
-
}
|
2756 |
-
|
2757 |
-
$lastChar = $num;
|
2758 |
-
}
|
2759 |
-
}
|
2760 |
-
|
2761 |
-
// also need to adjust the widths for the differences array
|
2762 |
-
if (isset($options['differences'])) {
|
2763 |
-
foreach ($options['differences'] as $charNum => $charName) {
|
2764 |
-
if ($charNum > $lastChar) {
|
2765 |
-
if (!$font['isUnicode']) {
|
2766 |
-
// With Unicode, widths array isn't used
|
2767 |
-
for ($i = $lastChar + 1; $i <= $charNum; $i++) {
|
2768 |
-
$widths[] = 0;
|
2769 |
-
}
|
2770 |
-
}
|
2771 |
-
|
2772 |
-
$lastChar = $charNum;
|
2773 |
-
}
|
2774 |
-
|
2775 |
-
if (isset($font['C'][$charName])) {
|
2776 |
-
$widths[$charNum - $firstChar] = $font['C'][$charName];
|
2777 |
-
if ($font['isUnicode']) {
|
2778 |
-
$cid_widths[$charName] = $font['C'][$charName];
|
2779 |
-
}
|
2780 |
-
}
|
2781 |
-
}
|
2782 |
-
}
|
2783 |
-
|
2784 |
-
if ($font['isUnicode']) {
|
2785 |
-
$font['CIDWidths'] = $cid_widths;
|
2786 |
-
}
|
2787 |
-
|
2788 |
-
$this->addMessage('selectFont: FirstChar = ' . $firstChar);
|
2789 |
-
$this->addMessage('selectFont: LastChar = ' . $lastChar);
|
2790 |
-
|
2791 |
-
$widthid = -1;
|
2792 |
-
|
2793 |
-
if (!$font['isUnicode']) {
|
2794 |
-
// With Unicode, widths array isn't used
|
2795 |
-
|
2796 |
-
$this->numObj++;
|
2797 |
-
$this->o_contents($this->numObj, 'new', 'raw');
|
2798 |
-
$this->objects[$this->numObj]['c'] .= '[' . implode(' ', $widths) . ']';
|
2799 |
-
$widthid = $this->numObj;
|
2800 |
-
}
|
2801 |
-
|
2802 |
-
$missing_width = 500;
|
2803 |
-
$stemV = 70;
|
2804 |
-
|
2805 |
-
if (isset($font['MissingWidth'])) {
|
2806 |
-
$missing_width = $font['MissingWidth'];
|
2807 |
-
}
|
2808 |
-
if (isset($font['StdVW'])) {
|
2809 |
-
$stemV = $font['StdVW'];
|
2810 |
-
} else {
|
2811 |
-
if (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) {
|
2812 |
-
$stemV = 120;
|
2813 |
-
}
|
2814 |
-
}
|
2815 |
-
|
2816 |
-
// load the pfb file, and put that into an object too.
|
2817 |
-
// note that pdf supports only binary format type 1 font files, though there is a
|
2818 |
-
// simple utility to convert them from pfa to pfb.
|
2819 |
-
// FIXME: should we move font subset creation to CPDF::output? See notes in issue #750.
|
2820 |
-
if (!$this->isUnicode || strtolower($fbtype) !== 'ttf' || empty($this->stringSubsets)) {
|
2821 |
-
$data = file_get_contents($fbfile);
|
2822 |
-
} else {
|
2823 |
-
$this->stringSubsets[$fontName][] = 32; // Force space if not in yet
|
2824 |
-
|
2825 |
-
$subset = $this->stringSubsets[$fontName];
|
2826 |
-
sort($subset);
|
2827 |
-
|
2828 |
-
// Load font
|
2829 |
-
$font_obj = Font::load($fbfile);
|
2830 |
-
$font_obj->parse();
|
2831 |
-
|
2832 |
-
// Define subset
|
2833 |
-
$font_obj->setSubset($subset);
|
2834 |
-
$font_obj->reduce();
|
2835 |
-
|
2836 |
-
// Write new font
|
2837 |
-
$tmp_name = $this->tmp . "/" . basename($fbfile) . ".tmp." . uniqid();
|
2838 |
-
$font_obj->open($tmp_name, BinaryStream::modeWrite);
|
2839 |
-
$font_obj->encode(array("OS/2"));
|
2840 |
-
$font_obj->close();
|
2841 |
-
|
2842 |
-
// Parse the new font to get cid2gid and widths
|
2843 |
-
$font_obj = Font::load($tmp_name);
|
2844 |
-
|
2845 |
-
// Find Unicode char map table
|
2846 |
-
$subtable = null;
|
2847 |
-
foreach ($font_obj->getData("cmap", "subtables") as $_subtable) {
|
2848 |
-
if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
|
2849 |
-
$subtable = $_subtable;
|
2850 |
-
break;
|
2851 |
-
}
|
2852 |
-
}
|
2853 |
-
|
2854 |
-
if ($subtable) {
|
2855 |
-
$glyphIndexArray = $subtable["glyphIndexArray"];
|
2856 |
-
$hmtx = $font_obj->getData("hmtx");
|
2857 |
-
|
2858 |
-
unset($glyphIndexArray[0xFFFF]);
|
2859 |
-
|
2860 |
-
$cidtogid = str_pad('', max(array_keys($glyphIndexArray)) * 2 + 1, "\x00");
|
2861 |
-
$font['CIDWidths'] = array();
|
2862 |
-
foreach ($glyphIndexArray as $cid => $gid) {
|
2863 |
-
if ($cid >= 0 && $cid < 0xFFFF && $gid) {
|
2864 |
-
$cidtogid[$cid * 2] = chr($gid >> 8);
|
2865 |
-
$cidtogid[$cid * 2 + 1] = chr($gid & 0xFF);
|
2866 |
-
}
|
2867 |
-
|
2868 |
-
$width = $font_obj->normalizeFUnit(isset($hmtx[$gid]) ? $hmtx[$gid][0] : $hmtx[0][0]);
|
2869 |
-
$font['CIDWidths'][$cid] = $width;
|
2870 |
-
}
|
2871 |
-
|
2872 |
-
$font['CIDtoGID'] = base64_encode(gzcompress($cidtogid));
|
2873 |
-
$font['CIDtoGID_Compressed'] = true;
|
2874 |
-
|
2875 |
-
$data = file_get_contents($tmp_name);
|
2876 |
-
} else {
|
2877 |
-
$data = file_get_contents($fbfile);
|
2878 |
-
}
|
2879 |
-
|
2880 |
-
$font_obj->close();
|
2881 |
-
unlink($tmp_name);
|
2882 |
-
}
|
2883 |
-
|
2884 |
-
// create the font descriptor
|
2885 |
-
$this->numObj++;
|
2886 |
-
$fontDescriptorId = $this->numObj;
|
2887 |
-
|
2888 |
-
$this->numObj++;
|
2889 |
-
$pfbid = $this->numObj;
|
2890 |
-
|
2891 |
-
// determine flags (more than a little flakey, hopefully will not matter much)
|
2892 |
-
$flags = 0;
|
2893 |
-
|
2894 |
-
if ($font['ItalicAngle'] != 0) {
|
2895 |
-
$flags += pow(2, 6);
|
2896 |
-
}
|
2897 |
-
|
2898 |
-
if ($font['IsFixedPitch'] === 'true') {
|
2899 |
-
$flags += 1;
|
2900 |
-
}
|
2901 |
-
|
2902 |
-
$flags += pow(2, 5); // assume non-sybolic
|
2903 |
-
$list = array(
|
2904 |
-
'Ascent' => 'Ascender',
|
2905 |
-
'CapHeight' => 'Ascender', //FIXME: php-font-lib is not grabbing this value, so we'll fake it and use the Ascender value // 'CapHeight'
|
2906 |
-
'MissingWidth' => 'MissingWidth',
|
2907 |
-
'Descent' => 'Descender',
|
2908 |
-
'FontBBox' => 'FontBBox',
|
2909 |
-
'ItalicAngle' => 'ItalicAngle'
|
2910 |
-
);
|
2911 |
-
$fdopt = array(
|
2912 |
-
'Flags' => $flags,
|
2913 |
-
'FontName' => $adobeFontName,
|
2914 |
-
'StemV' => $stemV
|
2915 |
-
);
|
2916 |
-
|
2917 |
-
foreach ($list as $k => $v) {
|
2918 |
-
if (isset($font[$v])) {
|
2919 |
-
$fdopt[$k] = $font[$v];
|
2920 |
-
}
|
2921 |
-
}
|
2922 |
-
|
2923 |
-
if (strtolower($fbtype) === 'pfb') {
|
2924 |
-
$fdopt['FontFile'] = $pfbid;
|
2925 |
-
} elseif (strtolower($fbtype) === 'ttf') {
|
2926 |
-
$fdopt['FontFile2'] = $pfbid;
|
2927 |
-
}
|
2928 |
-
|
2929 |
-
$this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt);
|
2930 |
-
|
2931 |
-
// embed the font program
|
2932 |
-
$this->o_contents($this->numObj, 'new');
|
2933 |
-
$this->objects[$pfbid]['c'] .= $data;
|
2934 |
-
|
2935 |
-
// determine the cruicial lengths within this file
|
2936 |
-
if (strtolower($fbtype) === 'pfb') {
|
2937 |
-
$l1 = strpos($data, 'eexec') + 6;
|
2938 |
-
$l2 = strpos($data, '00000000') - $l1;
|
2939 |
-
$l3 = mb_strlen($data, '8bit') - $l2 - $l1;
|
2940 |
-
$this->o_contents(
|
2941 |
-
$this->numObj,
|
2942 |
-
'add',
|
2943 |
-
array('Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3)
|
2944 |
-
);
|
2945 |
-
} elseif (strtolower($fbtype) == 'ttf') {
|
2946 |
-
$l1 = mb_strlen($data, '8bit');
|
2947 |
-
$this->o_contents($this->numObj, 'add', array('Length1' => $l1));
|
2948 |
-
}
|
2949 |
-
|
2950 |
-
// tell the font object about all this new stuff
|
2951 |
-
$tmp = array(
|
2952 |
-
'BaseFont' => $adobeFontName,
|
2953 |
-
'MissingWidth' => $missing_width,
|
2954 |
-
'Widths' => $widthid,
|
2955 |
-
'FirstChar' => $firstChar,
|
2956 |
-
'LastChar' => $lastChar,
|
2957 |
-
'FontDescriptor' => $fontDescriptorId
|
2958 |
-
);
|
2959 |
-
|
2960 |
-
if (strtolower($fbtype) === 'ttf') {
|
2961 |
-
$tmp['SubType'] = 'TrueType';
|
2962 |
-
}
|
2963 |
-
|
2964 |
-
$this->addMessage("adding extra info to font.($fontObj)");
|
2965 |
-
|
2966 |
-
foreach ($tmp as $fk => $fv) {
|
2967 |
-
$this->addMessage("$fk : $fv");
|
2968 |
-
}
|
2969 |
-
|
2970 |
-
$this->o_font($fontObj, 'add', $tmp);
|
2971 |
-
} else {
|
2972 |
-
$this->addMessage(
|
2973 |
-
'selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts'
|
2974 |
-
);
|
2975 |
-
}
|
2976 |
-
|
2977 |
-
// also set the differences here, note that this means that these will take effect only the
|
2978 |
-
//first time that a font is selected, else they are ignored
|
2979 |
-
if (isset($options['differences'])) {
|
2980 |
-
$font['differences'] = $options['differences'];
|
2981 |
-
}
|
2982 |
-
}
|
2983 |
-
}
|
2984 |
-
|
2985 |
-
if ($set && isset($this->fonts[$fontName])) {
|
2986 |
-
// so if for some reason the font was not set in the last one then it will not be selected
|
2987 |
-
$this->currentBaseFont = $fontName;
|
2988 |
-
|
2989 |
-
// the next lines mean that if a new font is selected, then the current text state will be
|
2990 |
-
// applied to it as well.
|
2991 |
-
$this->currentFont = $this->currentBaseFont;
|
2992 |
-
$this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
|
2993 |
-
|
2994 |
-
//$this->setCurrentFont();
|
2995 |
-
}
|
2996 |
-
|
2997 |
-
return $this->currentFontNum;
|
2998 |
-
//return $this->numObj;
|
2999 |
-
}
|
3000 |
-
|
3001 |
-
/**
|
3002 |
-
* sets up the current font, based on the font families, and the current text state
|
3003 |
-
* note that this system is quite flexible, a bold-italic font can be completely different to a
|
3004 |
-
* italic-bold font, and even bold-bold will have to be defined within the family to have meaning
|
3005 |
-
* This function is to be called whenever the currentTextState is changed, it will update
|
3006 |
-
* the currentFont setting to whatever the appropriate family one is.
|
3007 |
-
* If the user calls selectFont themselves then that will reset the currentBaseFont, and the currentFont
|
3008 |
-
* This function will change the currentFont to whatever it should be, but will not change the
|
3009 |
-
* currentBaseFont.
|
3010 |
-
*/
|
3011 |
-
private function setCurrentFont()
|
3012 |
-
{
|
3013 |
-
// if (strlen($this->currentBaseFont) == 0){
|
3014 |
-
// // then assume an initial font
|
3015 |
-
// $this->selectFont($this->defaultFont);
|
3016 |
-
// }
|
3017 |
-
// $cf = substr($this->currentBaseFont,strrpos($this->currentBaseFont,'/')+1);
|
3018 |
-
// if (strlen($this->currentTextState)
|
3019 |
-
// && isset($this->fontFamilies[$cf])
|
3020 |
-
// && isset($this->fontFamilies[$cf][$this->currentTextState])){
|
3021 |
-
// // then we are in some state or another
|
3022 |
-
// // and this font has a family, and the current setting exists within it
|
3023 |
-
// // select the font, then return it
|
3024 |
-
// $nf = substr($this->currentBaseFont,0,strrpos($this->currentBaseFont,'/')+1).$this->fontFamilies[$cf][$this->currentTextState];
|
3025 |
-
// $this->selectFont($nf,'',0);
|
3026 |
-
// $this->currentFont = $nf;
|
3027 |
-
// $this->currentFontNum = $this->fonts[$nf]['fontNum'];
|
3028 |
-
// } else {
|
3029 |
-
// // the this font must not have the right family member for the current state
|
3030 |
-
// // simply assume the base font
|
3031 |
-
$this->currentFont = $this->currentBaseFont;
|
3032 |
-
$this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
|
3033 |
-
// }
|
3034 |
-
}
|
3035 |
-
|
3036 |
-
/**
|
3037 |
-
* function for the user to find out what the ID is of the first page that was created during
|
3038 |
-
* startup - useful if they wish to add something to it later.
|
3039 |
-
*
|
3040 |
-
* @return int
|
3041 |
-
*/
|
3042 |
-
function getFirstPageId()
|
3043 |
-
{
|
3044 |
-
return $this->firstPageId;
|
3045 |
-
}
|
3046 |
-
|
3047 |
-
/**
|
3048 |
-
* add content to the currently active object
|
3049 |
-
*
|
3050 |
-
* @param $content
|
3051 |
-
*/
|
3052 |
-
private function addContent($content)
|
3053 |
-
{
|
3054 |
-
$this->objects[$this->currentContents]['c'] .= $content;
|
3055 |
-
}
|
3056 |
-
|
3057 |
-
/**
|
3058 |
-
* sets the color for fill operations
|
3059 |
-
*
|
3060 |
-
* @param $color
|
3061 |
-
* @param bool $force
|
3062 |
-
*/
|
3063 |
-
function setColor($color, $force = false)
|
3064 |
-
{
|
3065 |
-
$new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
|
3066 |
-
|
3067 |
-
if (!$force && $this->currentColor == $new_color) {
|
3068 |
-
return;
|
3069 |
-
}
|
3070 |
-
|
3071 |
-
if (isset($new_color[3])) {
|
3072 |
-
$this->currentColor = $new_color;
|
3073 |
-
$this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F k", $this->currentColor));
|
3074 |
-
} else {
|
3075 |
-
if (isset($new_color[2])) {
|
3076 |
-
$this->currentColor = $new_color;
|
3077 |
-
$this->addContent(vsprintf("\n%.3F %.3F %.3F rg", $this->currentColor));
|
3078 |
-
}
|
3079 |
-
}
|
3080 |
-
}
|
3081 |
-
|
3082 |
-
/**
|
3083 |
-
* sets the color for fill operations
|
3084 |
-
*
|
3085 |
-
* @param $fillRule
|
3086 |
-
*/
|
3087 |
-
function setFillRule($fillRule)
|
3088 |
-
{
|
3089 |
-
if (!in_array($fillRule, array("nonzero", "evenodd"))) {
|
3090 |
-
return;
|
3091 |
-
}
|
3092 |
-
|
3093 |
-
$this->fillRule = $fillRule;
|
3094 |
-
}
|
3095 |
-
|
3096 |
-
/**
|
3097 |
-
* sets the color for stroke operations
|
3098 |
-
*
|
3099 |
-
* @param $color
|
3100 |
-
* @param bool $force
|
3101 |
-
*/
|
3102 |
-
function setStrokeColor($color, $force = false)
|
3103 |
-
{
|
3104 |
-
$new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
|
3105 |
-
|
3106 |
-
if (!$force && $this->currentStrokeColor == $new_color) {
|
3107 |
-
return;
|
3108 |
-
}
|
3109 |
-
|
3110 |
-
if (isset($new_color[3])) {
|
3111 |
-
$this->currentStrokeColor = $new_color;
|
3112 |
-
$this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F K", $this->currentStrokeColor));
|
3113 |
-
} else {
|
3114 |
-
if (isset($new_color[2])) {
|
3115 |
-
$this->currentStrokeColor = $new_color;
|
3116 |
-
$this->addContent(vsprintf("\n%.3F %.3F %.3F RG", $this->currentStrokeColor));
|
3117 |
-
}
|
3118 |
-
}
|
3119 |
-
}
|
3120 |
-
|
3121 |
-
/**
|
3122 |
-
* Set the graphics state for compositions
|
3123 |
-
*
|
3124 |
-
* @param $parameters
|
3125 |
-
*/
|
3126 |
-
function setGraphicsState($parameters)
|
3127 |
-
{
|
3128 |
-
// Create a new graphics state object if necessary
|
3129 |
-
if (($gstate = array_search($parameters, $this->gstates)) === false) {
|
3130 |
-
$this->numObj++;
|
3131 |
-
$this->o_extGState($this->numObj, 'new', $parameters);
|
3132 |
-
$gstate = $this->numStates;
|
3133 |
-
$this->gstates[$gstate] = $parameters;
|
3134 |
-
}
|
3135 |
-
$this->addContent("\n/GS$gstate gs");
|
3136 |
-
}
|
3137 |
-
|
3138 |
-
/**
|
3139 |
-
* Set current blend mode & opacity for lines.
|
3140 |
-
*
|
3141 |
-
* Valid blend modes are:
|
3142 |
-
*
|
3143 |
-
* Normal, Multiply, Screen, Overlay, Darken, Lighten,
|
3144 |
-
* ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
|
3145 |
-
* Exclusion
|
3146 |
-
*
|
3147 |
-
* @param string $mode the blend mode to use
|
3148 |
-
* @param float $opacity 0.0 fully transparent, 1.0 fully opaque
|
3149 |
-
*/
|
3150 |
-
function setLineTransparency($mode, $opacity)
|
3151 |
-
{
|
3152 |
-
static $blend_modes = array(
|
3153 |
-
"Normal",
|
3154 |
-
"Multiply",
|
3155 |
-
"Screen",
|
3156 |
-
"Overlay",
|
3157 |
-
"Darken",
|
3158 |
-
"Lighten",
|
3159 |
-
"ColorDogde",
|
3160 |
-
"ColorBurn",
|
3161 |
-
"HardLight",
|
3162 |
-
"SoftLight",
|
3163 |
-
"Difference",
|
3164 |
-
"Exclusion"
|
3165 |
-
);
|
3166 |
-
|
3167 |
-
if (!in_array($mode, $blend_modes)) {
|
3168 |
-
$mode = "Normal";
|
3169 |
-
}
|
3170 |
-
|
3171 |
-
// Only create a new graphics state if required
|
3172 |
-
if ($mode === $this->currentLineTransparency["mode"] &&
|
3173 |
-
$opacity == $this->currentLineTransparency["opacity"]
|
3174 |
-
) {
|
3175 |
-
return;
|
3176 |
-
}
|
3177 |
-
|
3178 |
-
$this->currentLineTransparency["mode"] = $mode;
|
3179 |
-
$this->currentLineTransparency["opacity"] = $opacity;
|
3180 |
-
|
3181 |
-
$options = array(
|
3182 |
-
"BM" => "/$mode",
|
3183 |
-
"CA" => (float)$opacity
|
3184 |
-
);
|
3185 |
-
|
3186 |
-
$this->setGraphicsState($options);
|
3187 |
-
}
|
3188 |
-
|
3189 |
-
/**
|
3190 |
-
* Set current blend mode & opacity for filled objects.
|
3191 |
-
*
|
3192 |
-
* Valid blend modes are:
|
3193 |
-
*
|
3194 |
-
* Normal, Multiply, Screen, Overlay, Darken, Lighten,
|
3195 |
-
* ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
|
3196 |
-
* Exclusion
|
3197 |
-
*
|
3198 |
-
* @param string $mode the blend mode to use
|
3199 |
-
* @param float $opacity 0.0 fully transparent, 1.0 fully opaque
|
3200 |
-
*/
|
3201 |
-
function setFillTransparency($mode, $opacity)
|
3202 |
-
{
|
3203 |
-
static $blend_modes = array(
|
3204 |
-
"Normal",
|
3205 |
-
"Multiply",
|
3206 |
-
"Screen",
|
3207 |
-
"Overlay",
|
3208 |
-
"Darken",
|
3209 |
-
"Lighten",
|
3210 |
-
"ColorDogde",
|
3211 |
-
"ColorBurn",
|
3212 |
-
"HardLight",
|
3213 |
-
"SoftLight",
|
3214 |
-
"Difference",
|
3215 |
-
"Exclusion"
|
3216 |
-
);
|
3217 |
-
|
3218 |
-
if (!in_array($mode, $blend_modes)) {
|
3219 |
-
$mode = "Normal";
|
3220 |
-
}
|
3221 |
-
|
3222 |
-
if ($mode === $this->currentFillTransparency["mode"] &&
|
3223 |
-
$opacity == $this->currentFillTransparency["opacity"]
|
3224 |
-
) {
|
3225 |
-
return;
|
3226 |
-
}
|
3227 |
-
|
3228 |
-
$this->currentFillTransparency["mode"] = $mode;
|
3229 |
-
$this->currentFillTransparency["opacity"] = $opacity;
|
3230 |
-
|
3231 |
-
$options = array(
|
3232 |
-
"BM" => "/$mode",
|
3233 |
-
"ca" => (float)$opacity,
|
3234 |
-
);
|
3235 |
-
|
3236 |
-
$this->setGraphicsState($options);
|
3237 |
-
}
|
3238 |
-
|
3239 |
-
/**
|
3240 |
-
* draw a line from one set of coordinates to another
|
3241 |
-
*
|
3242 |
-
* @param $x1
|
3243 |
-
* @param $y1
|
3244 |
-
* @param $x2
|
3245 |
-
* @param $y2
|
3246 |
-
* @param bool $stroke
|
3247 |
-
*/
|
3248 |
-
function line($x1, $y1, $x2, $y2, $stroke = true)
|
3249 |
-
{
|
3250 |
-
$this->addContent(sprintf("\n%.3F %.3F m %.3F %.3F l", $x1, $y1, $x2, $y2));
|
3251 |
-
|
3252 |
-
if ($stroke) {
|
3253 |
-
$this->addContent(' S');
|
3254 |
-
}
|
3255 |
-
}
|
3256 |
-
|
3257 |
-
/**
|
3258 |
-
* draw a bezier curve based on 4 control points
|
3259 |
-
*
|
3260 |
-
* @param $x0
|
3261 |
-
* @param $y0
|
3262 |
-
* @param $x1
|
3263 |
-
* @param $y1
|
3264 |
-
* @param $x2
|
3265 |
-
* @param $y2
|
3266 |
-
* @param $x3
|
3267 |
-
* @param $y3
|
3268 |
-
*/
|
3269 |
-
function curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
|
3270 |
-
{
|
3271 |
-
// in the current line style, draw a bezier curve from (x0,y0) to (x3,y3) using the other two points
|
3272 |
-
// as the control points for the curve.
|
3273 |
-
$this->addContent(
|
3274 |
-
sprintf("\n%.3F %.3F m %.3F %.3F %.3F %.3F %.3F %.3F c S", $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
|
3275 |
-
);
|
3276 |
-
}
|
3277 |
-
|
3278 |
-
/**
|
3279 |
-
* draw a part of an ellipse
|
3280 |
-
*
|
3281 |
-
* @param $x0
|
3282 |
-
* @param $y0
|
3283 |
-
* @param $astart
|
3284 |
-
* @param $afinish
|
3285 |
-
* @param $r1
|
3286 |
-
* @param int $r2
|
3287 |
-
* @param int $angle
|
3288 |
-
* @param int $nSeg
|
3289 |
-
*/
|
3290 |
-
function partEllipse($x0, $y0, $astart, $afinish, $r1, $r2 = 0, $angle = 0, $nSeg = 8)
|
3291 |
-
{
|
3292 |
-
$this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, false);
|
3293 |
-
}
|
3294 |
-
|
3295 |
-
/**
|
3296 |
-
* draw a filled ellipse
|
3297 |
-
*
|
3298 |
-
* @param $x0
|
3299 |
-
* @param $y0
|
3300 |
-
* @param $r1
|
3301 |
-
* @param int $r2
|
3302 |
-
* @param int $angle
|
3303 |
-
* @param int $nSeg
|
3304 |
-
* @param int $astart
|
3305 |
-
* @param int $afinish
|
3306 |
-
*/
|
3307 |
-
function filledEllipse($x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360)
|
3308 |
-
{
|
3309 |
-
$this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, true, true);
|
3310 |
-
}
|
3311 |
-
|
3312 |
-
/**
|
3313 |
-
* @param $x
|
3314 |
-
* @param $y
|
3315 |
-
*/
|
3316 |
-
function lineTo($x, $y)
|
3317 |
-
{
|
3318 |
-
$this->addContent(sprintf("\n%.3F %.3F l", $x, $y));
|
3319 |
-
}
|
3320 |
-
|
3321 |
-
/**
|
3322 |
-
* @param $x
|
3323 |
-
* @param $y
|
3324 |
-
*/
|
3325 |
-
function moveTo($x, $y)
|
3326 |
-
{
|
3327 |
-
$this->addContent(sprintf("\n%.3F %.3F m", $x, $y));
|
3328 |
-
}
|
3329 |
-
|
3330 |
-
/**
|
3331 |
-
* draw a bezier curve based on 4 control points
|
3332 |
-
*
|
3333 |
-
* @param $x1
|
3334 |
-
* @param $y1
|
3335 |
-
* @param $x2
|
3336 |
-
* @param $y2
|
3337 |
-
* @param $x3
|
3338 |
-
* @param $y3
|
3339 |
-
*/
|
3340 |
-
function curveTo($x1, $y1, $x2, $y2, $x3, $y3)
|
3341 |
-
{
|
3342 |
-
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F %.3F %.3F c", $x1, $y1, $x2, $y2, $x3, $y3));
|
3343 |
-
}
|
3344 |
-
|
3345 |
-
/**
|
3346 |
-
* draw a bezier curve based on 4 control points
|
3347 |
-
*/ function quadTo($cpx, $cpy, $x, $y)
|
3348 |
-
{
|
3349 |
-
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F v", $cpx, $cpy, $x, $y));
|
3350 |
-
}
|
3351 |
-
|
3352 |
-
function closePath()
|
3353 |
-
{
|
3354 |
-
$this->addContent(' h');
|
3355 |
-
}
|
3356 |
-
|
3357 |
-
function endPath()
|
3358 |
-
{
|
3359 |
-
$this->addContent(' n');
|
3360 |
-
}
|
3361 |
-
|
3362 |
-
/**
|
3363 |
-
* draw an ellipse
|
3364 |
-
* note that the part and filled ellipse are just special cases of this function
|
3365 |
-
*
|
3366 |
-
* draws an ellipse in the current line style
|
3367 |
-
* centered at $x0,$y0, radii $r1,$r2
|
3368 |
-
* if $r2 is not set, then a circle is drawn
|
3369 |
-
* from $astart to $afinish, measured in degrees, running anti-clockwise from the right hand side of the ellipse.
|
3370 |
-
* nSeg is not allowed to be less than 2, as this will simply draw a line (and will even draw a
|
3371 |
-
* pretty crappy shape at 2, as we are approximating with bezier curves.
|
3372 |
-
*
|
3373 |
-
* @param $x0
|
3374 |
-
* @param $y0
|
3375 |
-
* @param $r1
|
3376 |
-
* @param int $r2
|
3377 |
-
* @param int $angle
|
3378 |
-
* @param int $nSeg
|
3379 |
-
* @param int $astart
|
3380 |
-
* @param int $afinish
|
3381 |
-
* @param bool $close
|
3382 |
-
* @param bool $fill
|
3383 |
-
* @param bool $stroke
|
3384 |
-
* @param bool $incomplete
|
3385 |
-
*/
|
3386 |
-
function ellipse(
|
3387 |
-
$x0,
|
3388 |
-
$y0,
|
3389 |
-
$r1,
|
3390 |
-
$r2 = 0,
|
3391 |
-
$angle = 0,
|
3392 |
-
$nSeg = 8,
|
3393 |
-
$astart = 0,
|
3394 |
-
$afinish = 360,
|
3395 |
-
$close = true,
|
3396 |
-
$fill = false,
|
3397 |
-
$stroke = true,
|
3398 |
-
$incomplete = false
|
3399 |
-
) {
|
3400 |
-
if ($r1 == 0) {
|
3401 |
-
return;
|
3402 |
-
}
|
3403 |
-
|
3404 |
-
if ($r2 == 0) {
|
3405 |
-
$r2 = $r1;
|
3406 |
-
}
|
3407 |
-
|
3408 |
-
if ($nSeg < 2) {
|
3409 |
-
$nSeg = 2;
|
3410 |
-
}
|
3411 |
-
|
3412 |
-
$astart = deg2rad((float)$astart);
|
3413 |
-
$afinish = deg2rad((float)$afinish);
|
3414 |
-
$totalAngle = $afinish - $astart;
|
3415 |
-
|
3416 |
-
$dt = $totalAngle / $nSeg;
|
3417 |
-
$dtm = $dt / 3;
|
3418 |
-
|
3419 |
-
if ($angle != 0) {
|
3420 |
-
$a = -1 * deg2rad((float)$angle);
|
3421 |
-
|
3422 |
-
$this->addContent(
|
3423 |
-
sprintf("\n q %.3F %.3F %.3F %.3F %.3F %.3F cm", cos($a), -sin($a), sin($a), cos($a), $x0, $y0)
|
3424 |
-
);
|
3425 |
-
|
3426 |
-
$x0 = 0;
|
3427 |
-
$y0 = 0;
|
3428 |
-
}
|
3429 |
-
|
3430 |
-
$t1 = $astart;
|
3431 |
-
$a0 = $x0 + $r1 * cos($t1);
|
3432 |
-
$b0 = $y0 + $r2 * sin($t1);
|
3433 |
-
$c0 = -$r1 * sin($t1);
|
3434 |
-
$d0 = $r2 * cos($t1);
|
3435 |
-
|
3436 |
-
if (!$incomplete) {
|
3437 |
-
$this->addContent(sprintf("\n%.3F %.3F m ", $a0, $b0));
|
3438 |
-
}
|
3439 |
-
|
3440 |
-
for ($i = 1; $i <= $nSeg; $i++) {
|
3441 |
-
// draw this bit of the total curve
|
3442 |
-
$t1 = $i * $dt + $astart;
|
3443 |
-
$a1 = $x0 + $r1 * cos($t1);
|
3444 |
-
$b1 = $y0 + $r2 * sin($t1);
|
3445 |
-
$c1 = -$r1 * sin($t1);
|
3446 |
-
$d1 = $r2 * cos($t1);
|
3447 |
-
|
3448 |
-
$this->addContent(
|
3449 |
-
sprintf(
|
3450 |
-
"\n%.3F %.3F %.3F %.3F %.3F %.3F c",
|
3451 |
-
($a0 + $c0 * $dtm),
|
3452 |
-
($b0 + $d0 * $dtm),
|
3453 |
-
($a1 - $c1 * $dtm),
|
3454 |
-
($b1 - $d1 * $dtm),
|
3455 |
-
$a1,
|
3456 |
-
$b1
|
3457 |
-
)
|
3458 |
-
);
|
3459 |
-
|
3460 |
-
$a0 = $a1;
|
3461 |
-
$b0 = $b1;
|
3462 |
-
$c0 = $c1;
|
3463 |
-
$d0 = $d1;
|
3464 |
-
}
|
3465 |
-
|
3466 |
-
if (!$incomplete) {
|
3467 |
-
if ($fill) {
|
3468 |
-
$this->addContent(' f');
|
3469 |
-
}
|
3470 |
-
|
3471 |
-
if ($stroke) {
|
3472 |
-
if ($close) {
|
3473 |
-
$this->addContent(' s'); // small 's' signifies closing the path as well
|
3474 |
-
} else {
|
3475 |
-
$this->addContent(' S');
|
3476 |
-
}
|
3477 |
-
}
|
3478 |
-
}
|
3479 |
-
|
3480 |
-
if ($angle != 0) {
|
3481 |
-
$this->addContent(' Q');
|
3482 |
-
}
|
3483 |
-
}
|
3484 |
-
|
3485 |
-
/**
|
3486 |
-
* this sets the line drawing style.
|
3487 |
-
* width, is the thickness of the line in user units
|
3488 |
-
* cap is the type of cap to put on the line, values can be 'butt','round','square'
|
3489 |
-
* where the diffference between 'square' and 'butt' is that 'square' projects a flat end past the
|
3490 |
-
* end of the line.
|
3491 |
-
* join can be 'miter', 'round', 'bevel'
|
3492 |
-
* dash is an array which sets the dash pattern, is a series of length values, which are the lengths of the
|
3493 |
-
* on and off dashes.
|
3494 |
-
* (2) represents 2 on, 2 off, 2 on , 2 off ...
|
3495 |
-
* (2,1) is 2 on, 1 off, 2 on, 1 off.. etc
|
3496 |
-
* phase is a modifier on the dash pattern which is used to shift the point at which the pattern starts.
|
3497 |
-
*
|
3498 |
-
* @param int $width
|
3499 |
-
* @param string $cap
|
3500 |
-
* @param string $join
|
3501 |
-
* @param string $dash
|
3502 |
-
* @param int $phase
|
3503 |
-
*/
|
3504 |
-
function setLineStyle($width = 1, $cap = '', $join = '', $dash = '', $phase = 0)
|
3505 |
-
{
|
3506 |
-
// this is quite inefficient in that it sets all the parameters whenever 1 is changed, but will fix another day
|
3507 |
-
$string = '';
|
3508 |
-
|
3509 |
-
if ($width > 0) {
|
3510 |
-
$string .= "$width w";
|
3511 |
-
}
|
3512 |
-
|
3513 |
-
$ca = array('butt' => 0, 'round' => 1, 'square' => 2);
|
3514 |
-
|
3515 |
-
if (isset($ca[$cap])) {
|
3516 |
-
$string .= " $ca[$cap] J";
|
3517 |
-
}
|
3518 |
-
|
3519 |
-
$ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
|
3520 |
-
|
3521 |
-
if (isset($ja[$join])) {
|
3522 |
-
$string .= " $ja[$join] j";
|
3523 |
-
}
|
3524 |
-
|
3525 |
-
if (is_array($dash)) {
|
3526 |
-
$string .= ' [ ' . implode(' ', $dash) . " ] $phase d";
|
3527 |
-
}
|
3528 |
-
|
3529 |
-
$this->currentLineStyle = $string;
|
3530 |
-
$this->addContent("\n$string");
|
3531 |
-
}
|
3532 |
-
|
3533 |
-
/**
|
3534 |
-
* draw a polygon, the syntax for this is similar to the GD polygon command
|
3535 |
-
*
|
3536 |
-
* @param $p
|
3537 |
-
* @param $np
|
3538 |
-
* @param bool $f
|
3539 |
-
*/
|
3540 |
-
function polygon($p, $np, $f = false)
|
3541 |
-
{
|
3542 |
-
$this->addContent(sprintf("\n%.3F %.3F m ", $p[0], $p[1]));
|
3543 |
-
|
3544 |
-
for ($i = 2; $i < $np * 2; $i = $i + 2) {
|
3545 |
-
$this->addContent(sprintf("%.3F %.3F l ", $p[$i], $p[$i + 1]));
|
3546 |
-
}
|
3547 |
-
|
3548 |
-
if ($f) {
|
3549 |
-
$this->addContent(' f');
|
3550 |
-
} else {
|
3551 |
-
$this->addContent(' S');
|
3552 |
-
}
|
3553 |
-
}
|
3554 |
-
|
3555 |
-
/**
|
3556 |
-
* a filled rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
3557 |
-
* the coordinates of the upper-right corner
|
3558 |
-
*
|
3559 |
-
* @param $x1
|
3560 |
-
* @param $y1
|
3561 |
-
* @param $width
|
3562 |
-
* @param $height
|
3563 |
-
*/
|
3564 |
-
function filledRectangle($x1, $y1, $width, $height)
|
3565 |
-
{
|
3566 |
-
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re f", $x1, $y1, $width, $height));
|
3567 |
-
}
|
3568 |
-
|
3569 |
-
/**
|
3570 |
-
* draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
3571 |
-
* the coordinates of the upper-right corner
|
3572 |
-
*
|
3573 |
-
* @param $x1
|
3574 |
-
* @param $y1
|
3575 |
-
* @param $width
|
3576 |
-
* @param $height
|
3577 |
-
*/
|
3578 |
-
function rectangle($x1, $y1, $width, $height)
|
3579 |
-
{
|
3580 |
-
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re S", $x1, $y1, $width, $height));
|
3581 |
-
}
|
3582 |
-
|
3583 |
-
/**
|
3584 |
-
* draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
3585 |
-
* the coordinates of the upper-right corner
|
3586 |
-
*
|
3587 |
-
* @param $x1
|
3588 |
-
* @param $y1
|
3589 |
-
* @param $width
|
3590 |
-
* @param $height
|
3591 |
-
*/
|
3592 |
-
function rect($x1, $y1, $width, $height)
|
3593 |
-
{
|
3594 |
-
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re", $x1, $y1, $width, $height));
|
3595 |
-
}
|
3596 |
-
|
3597 |
-
function stroke()
|
3598 |
-
{
|
3599 |
-
$this->addContent("\nS");
|
3600 |
-
}
|
3601 |
-
|
3602 |
-
function fill()
|
3603 |
-
{
|
3604 |
-
$this->addContent("\nf" . ($this->fillRule === "evenodd" ? "*" : ""));
|
3605 |
-
}
|
3606 |
-
|
3607 |
-
function fillStroke()
|
3608 |
-
{
|
3609 |
-
$this->addContent("\nb" . ($this->fillRule === "evenodd" ? "*" : ""));
|
3610 |
-
}
|
3611 |
-
|
3612 |
-
/**
|
3613 |
-
* save the current graphic state
|
3614 |
-
*/
|
3615 |
-
function save()
|
3616 |
-
{
|
3617 |
-
// we must reset the color cache or it will keep bad colors after clipping
|
3618 |
-
$this->currentColor = null;
|
3619 |
-
$this->currentStrokeColor = null;
|
3620 |
-
$this->addContent("\nq");
|
3621 |
-
}
|
3622 |
-
|
3623 |
-
/**
|
3624 |
-
* restore the last graphic state
|
3625 |
-
*/
|
3626 |
-
function restore()
|
3627 |
-
{
|
3628 |
-
// we must reset the color cache or it will keep bad colors after clipping
|
3629 |
-
$this->currentColor = null;
|
3630 |
-
$this->currentStrokeColor = null;
|
3631 |
-
$this->addContent("\nQ");
|
3632 |
-
}
|
3633 |
-
|
3634 |
-
/**
|
3635 |
-
* draw a clipping rectangle, all the elements added after this will be clipped
|
3636 |
-
*
|
3637 |
-
* @param $x1
|
3638 |
-
* @param $y1
|
3639 |
-
* @param $width
|
3640 |
-
* @param $height
|
3641 |
-
*/
|
3642 |
-
function clippingRectangle($x1, $y1, $width, $height)
|
3643 |
-
{
|
3644 |
-
$this->save();
|
3645 |
-
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re W n", $x1, $y1, $width, $height));
|
3646 |
-
}
|
3647 |
-
|
3648 |
-
/**
|
3649 |
-
* draw a clipping rounded rectangle, all the elements added after this will be clipped
|
3650 |
-
*
|
3651 |
-
* @param $x1
|
3652 |
-
* @param $y1
|
3653 |
-
* @param $w
|
3654 |
-
* @param $h
|
3655 |
-
* @param $rTL
|
3656 |
-
* @param $rTR
|
3657 |
-
* @param $rBR
|
3658 |
-
* @param $rBL
|
3659 |
-
*/
|
3660 |
-
function clippingRectangleRounded($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
|
3661 |
-
{
|
3662 |
-
$this->save();
|
3663 |
-
|
3664 |
-
// start: top edge, left end
|
3665 |
-
$this->addContent(sprintf("\n%.3F %.3F m ", $x1, $y1 - $rTL + $h));
|
3666 |
-
|
3667 |
-
// line: bottom edge, left end
|
3668 |
-
$this->addContent(sprintf("\n%.3F %.3F l ", $x1, $y1 + $rBL));
|
3669 |
-
|
3670 |
-
// curve: bottom-left corner
|
3671 |
-
$this->ellipse($x1 + $rBL, $y1 + $rBL, $rBL, 0, 0, 8, 180, 270, false, false, false, true);
|
3672 |
-
|
3673 |
-
// line: right edge, bottom end
|
3674 |
-
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w - $rBR, $y1));
|
3675 |
-
|
3676 |
-
// curve: bottom-right corner
|
3677 |
-
$this->ellipse($x1 + $w - $rBR, $y1 + $rBR, $rBR, 0, 0, 8, 270, 360, false, false, false, true);
|
3678 |
-
|
3679 |
-
// line: right edge, top end
|
3680 |
-
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w, $y1 + $h - $rTR));
|
3681 |
-
|
3682 |
-
// curve: bottom-right corner
|
3683 |
-
$this->ellipse($x1 + $w - $rTR, $y1 + $h - $rTR, $rTR, 0, 0, 8, 0, 90, false, false, false, true);
|
3684 |
-
|
3685 |
-
// line: bottom edge, right end
|
3686 |
-
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rTL, $y1 + $h));
|
3687 |
-
|
3688 |
-
// curve: top-right corner
|
3689 |
-
$this->ellipse($x1 + $rTL, $y1 + $h - $rTL, $rTL, 0, 0, 8, 90, 180, false, false, false, true);
|
3690 |
-
|
3691 |
-
// line: top edge, left end
|
3692 |
-
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rBL, $y1));
|
3693 |
-
|
3694 |
-
// Close & clip
|
3695 |
-
$this->addContent(" W n");
|
3696 |
-
}
|
3697 |
-
|
3698 |
-
/**
|
3699 |
-
* ends the last clipping shape
|
3700 |
-
*/
|
3701 |
-
function clippingEnd()
|
3702 |
-
{
|
3703 |
-
$this->restore();
|
3704 |
-
}
|
3705 |
-
|
3706 |
-
/**
|
3707 |
-
* scale
|
3708 |
-
*
|
3709 |
-
* @param float $s_x scaling factor for width as percent
|
3710 |
-
* @param float $s_y scaling factor for height as percent
|
3711 |
-
* @param float $x Origin abscissa
|
3712 |
-
* @param float $y Origin ordinate
|
3713 |
-
*/
|
3714 |
-
function scale($s_x, $s_y, $x, $y)
|
3715 |
-
{
|
3716 |
-
$y = $this->currentPageSize["height"] - $y;
|
3717 |
-
|
3718 |
-
$tm = array(
|
3719 |
-
$s_x,
|
3720 |
-
0,
|
3721 |
-
0,
|
3722 |
-
$s_y,
|
3723 |
-
$x * (1 - $s_x),
|
3724 |
-
$y * (1 - $s_y)
|
3725 |
-
);
|
3726 |
-
|
3727 |
-
$this->transform($tm);
|
3728 |
-
}
|
3729 |
-
|
3730 |
-
/**
|
3731 |
-
* translate
|
3732 |
-
*
|
3733 |
-
* @param float $t_x movement to the right
|
3734 |
-
* @param float $t_y movement to the bottom
|
3735 |
-
*/
|
3736 |
-
function translate($t_x, $t_y)
|
3737 |
-
{
|
3738 |
-
$tm = array(
|
3739 |
-
1,
|
3740 |
-
0,
|
3741 |
-
0,
|
3742 |
-
1,
|
3743 |
-
$t_x,
|
3744 |
-
-$t_y
|
3745 |
-
);
|
3746 |
-
|
3747 |
-
$this->transform($tm);
|
3748 |
-
}
|
3749 |
-
|
3750 |
-
/**
|
3751 |
-
* rotate
|
3752 |
-
*
|
3753 |
-
* @param float $angle angle in degrees for counter-clockwise rotation
|
3754 |
-
* @param float $x Origin abscissa
|
3755 |
-
* @param float $y Origin ordinate
|
3756 |
-
*/
|
3757 |
-
function rotate($angle, $x, $y)
|
3758 |
-
{
|
3759 |
-
$y = $this->currentPageSize["height"] - $y;
|
3760 |
-
|
3761 |
-
$a = deg2rad($angle);
|
3762 |
-
$cos_a = cos($a);
|
3763 |
-
$sin_a = sin($a);
|
3764 |
-
|
3765 |
-
$tm = array(
|
3766 |
-
$cos_a,
|
3767 |
-
-$sin_a,
|
3768 |
-
$sin_a,
|
3769 |
-
$cos_a,
|
3770 |
-
$x - $sin_a * $y - $cos_a * $x,
|
3771 |
-
$y - $cos_a * $y + $sin_a * $x,
|
3772 |
-
);
|
3773 |
-
|
3774 |
-
$this->transform($tm);
|
3775 |
-
}
|
3776 |
-
|
3777 |
-
/**
|
3778 |
-
* skew
|
3779 |
-
*
|
3780 |
-
* @param float $angle_x
|
3781 |
-
* @param float $angle_y
|
3782 |
-
* @param float $x Origin abscissa
|
3783 |
-
* @param float $y Origin ordinate
|
3784 |
-
*/
|
3785 |
-
function skew($angle_x, $angle_y, $x, $y)
|
3786 |
-
{
|
3787 |
-
$y = $this->currentPageSize["height"] - $y;
|
3788 |
-
|
3789 |
-
$tan_x = tan(deg2rad($angle_x));
|
3790 |
-
$tan_y = tan(deg2rad($angle_y));
|
3791 |
-
|
3792 |
-
$tm = array(
|
3793 |
-
1,
|
3794 |
-
-$tan_y,
|
3795 |
-
-$tan_x,
|
3796 |
-
1,
|
3797 |
-
$tan_x * $y,
|
3798 |
-
$tan_y * $x,
|
3799 |
-
);
|
3800 |
-
|
3801 |
-
$this->transform($tm);
|
3802 |
-
}
|
3803 |
-
|
3804 |
-
/**
|
3805 |
-
* apply graphic transformations
|
3806 |
-
*
|
3807 |
-
* @param array $tm transformation matrix
|
3808 |
-
*/
|
3809 |
-
function transform($tm)
|
3810 |
-
{
|
3811 |
-
$this->addContent(vsprintf("\n %.3F %.3F %.3F %.3F %.3F %.3F cm", $tm));
|
3812 |
-
}
|
3813 |
-
|
3814 |
-
/**
|
3815 |
-
* add a new page to the document
|
3816 |
-
* this also makes the new page the current active object
|
3817 |
-
*
|
3818 |
-
* @param int $insert
|
3819 |
-
* @param int $id
|
3820 |
-
* @param string $pos
|
3821 |
-
* @return int
|
3822 |
-
*/
|
3823 |
-
function newPage($insert = 0, $id = 0, $pos = 'after')
|
3824 |
-
{
|
3825 |
-
// if there is a state saved, then go up the stack closing them
|
3826 |
-
// then on the new page, re-open them with the right setings
|
3827 |
-
|
3828 |
-
if ($this->nStateStack) {
|
3829 |
-
for ($i = $this->nStateStack; $i >= 1; $i--) {
|
3830 |
-
$this->restoreState($i);
|
3831 |
-
}
|
3832 |
-
}
|
3833 |
-
|
3834 |
-
$this->numObj++;
|
3835 |
-
|
3836 |
-
if ($insert) {
|
3837 |
-
// the id from the ezPdf class is the id of the contents of the page, not the page object itself
|
3838 |
-
// query that object to find the parent
|
3839 |
-
$rid = $this->objects[$id]['onPage'];
|
3840 |
-
$opt = array('rid' => $rid, 'pos' => $pos);
|
3841 |
-
$this->o_page($this->numObj, 'new', $opt);
|
3842 |
-
} else {
|
3843 |
-
$this->o_page($this->numObj, 'new');
|
3844 |
-
}
|
3845 |
-
|
3846 |
-
// if there is a stack saved, then put that onto the page
|
3847 |
-
if ($this->nStateStack) {
|
3848 |
-
for ($i = 1; $i <= $this->nStateStack; $i++) {
|
3849 |
-
$this->saveState($i);
|
3850 |
-
}
|
3851 |
-
}
|
3852 |
-
|
3853 |
-
// and if there has been a stroke or fill color set, then transfer them
|
3854 |
-
if (isset($this->currentColor)) {
|
3855 |
-
$this->setColor($this->currentColor, true);
|
3856 |
-
}
|
3857 |
-
|
3858 |
-
if (isset($this->currentStrokeColor)) {
|
3859 |
-
$this->setStrokeColor($this->currentStrokeColor, true);
|
3860 |
-
}
|
3861 |
-
|
3862 |
-
// if there is a line style set, then put this in too
|
3863 |
-
if (mb_strlen($this->currentLineStyle, '8bit')) {
|
3864 |
-
$this->addContent("\n$this->currentLineStyle");
|
3865 |
-
}
|
3866 |
-
|
3867 |
-
// the call to the o_page object set currentContents to the present page, so this can be returned as the page id
|
3868 |
-
return $this->currentContents;
|
3869 |
-
}
|
3870 |
-
|
3871 |
-
/**
|
3872 |
-
* Streams the PDF to the client.
|
3873 |
-
*
|
3874 |
-
* @param string $filename The filename to present to the client.
|
3875 |
-
* @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1).
|
3876 |
-
*/
|
3877 |
-
function stream($filename = "document.pdf", $options = array())
|
3878 |
-
{
|
3879 |
-
if (headers_sent()) {
|
3880 |
-
die("Unable to stream pdf: headers already sent");
|
3881 |
-
}
|
3882 |
-
|
3883 |
-
if (!isset($options["compress"])) $options["compress"] = true;
|
3884 |
-
if (!isset($options["Attachment"])) $options["Attachment"] = true;
|
3885 |
-
|
3886 |
-
$debug = !$options['compress'];
|
3887 |
-
$tmp = ltrim($this->output($debug));
|
3888 |
-
|
3889 |
-
header("Cache-Control: private");
|
3890 |
-
header("Content-Type: application/pdf");
|
3891 |
-
header("Content-Length: " . mb_strlen($tmp, "8bit"));
|
3892 |
-
|
3893 |
-
$filename = str_replace(array("\n", "'"), "", basename($filename, ".pdf")) . ".pdf";
|
3894 |
-
$attachment = $options["Attachment"] ? "attachment" : "inline";
|
3895 |
-
|
3896 |
-
$encoding = mb_detect_encoding($filename);
|
3897 |
-
$fallbackfilename = mb_convert_encoding($filename, "ISO-8859-1", $encoding);
|
3898 |
-
$fallbackfilename = str_replace("\"", "", $fallbackfilename);
|
3899 |
-
$encodedfilename = rawurlencode($filename);
|
3900 |
-
|
3901 |
-
$contentDisposition = "Content-Disposition: $attachment; filename=\"$fallbackfilename\"";
|
3902 |
-
if ($fallbackfilename !== $filename) {
|
3903 |
-
$contentDisposition .= "; filename*=UTF-8''$encodedfilename";
|
3904 |
-
}
|
3905 |
-
header($contentDisposition);
|
3906 |
-
|
3907 |
-
echo $tmp;
|
3908 |
-
flush();
|
3909 |
-
}
|
3910 |
-
|
3911 |
-
/**
|
3912 |
-
* return the height in units of the current font in the given size
|
3913 |
-
*
|
3914 |
-
* @param $size
|
3915 |
-
* @return float|int
|
3916 |
-
*/
|
3917 |
-
function getFontHeight($size)
|
3918 |
-
{
|
3919 |
-
if (!$this->numFonts) {
|
3920 |
-
$this->selectFont($this->defaultFont);
|
3921 |
-
}
|
3922 |
-
|
3923 |
-
$font = $this->fonts[$this->currentFont];
|
3924 |
-
|
3925 |
-
// for the current font, and the given size, what is the height of the font in user units
|
3926 |
-
if (isset($font['Ascender']) && isset($font['Descender'])) {
|
3927 |
-
$h = $font['Ascender'] - $font['Descender'];
|
3928 |
-
} else {
|
3929 |
-
$h = $font['FontBBox'][3] - $font['FontBBox'][1];
|
3930 |
-
}
|
3931 |
-
|
3932 |
-
// have to adjust by a font offset for Windows fonts. unfortunately it looks like
|
3933 |
-
// the bounding box calculations are wrong and I don't know why.
|
3934 |
-
if (isset($font['FontHeightOffset'])) {
|
3935 |
-
// For CourierNew from Windows this needs to be -646 to match the
|
3936 |
-
// Adobe native Courier font.
|
3937 |
-
//
|
3938 |
-
// For FreeMono from GNU this needs to be -337 to match the
|
3939 |
-
// Courier font.
|
3940 |
-
//
|
3941 |
-
// Both have been added manually to the .afm and .ufm files.
|
3942 |
-
$h += (int)$font['FontHeightOffset'];
|
3943 |
-
}
|
3944 |
-
|
3945 |
-
return $size * $h / 1000;
|
3946 |
-
}
|
3947 |
-
|
3948 |
-
/**
|
3949 |
-
* @param $size
|
3950 |
-
* @return float|int
|
3951 |
-
*/
|
3952 |
-
function getFontXHeight($size)
|
3953 |
-
{
|
3954 |
-
if (!$this->numFonts) {
|
3955 |
-
$this->selectFont($this->defaultFont);
|
3956 |
-
}
|
3957 |
-
|
3958 |
-
$font = $this->fonts[$this->currentFont];
|
3959 |
-
|
3960 |
-
// for the current font, and the given size, what is the height of the font in user units
|
3961 |
-
if (isset($font['XHeight'])) {
|
3962 |
-
$xh = $font['Ascender'] - $font['Descender'];
|
3963 |
-
} else {
|
3964 |
-
$xh = $this->getFontHeight($size) / 2;
|
3965 |
-
}
|
3966 |
-
|
3967 |
-
return $size * $xh / 1000;
|
3968 |
-
}
|
3969 |
-
|
3970 |
-
/**
|
3971 |
-
* return the font descender, this will normally return a negative number
|
3972 |
-
* if you add this number to the baseline, you get the level of the bottom of the font
|
3973 |
-
* it is in the pdf user units
|
3974 |
-
*
|
3975 |
-
* @param $size
|
3976 |
-
* @return float|int
|
3977 |
-
*/
|
3978 |
-
function getFontDescender($size)
|
3979 |
-
{
|
3980 |
-
// note that this will most likely return a negative value
|
3981 |
-
if (!$this->numFonts) {
|
3982 |
-
$this->selectFont($this->defaultFont);
|
3983 |
-
}
|
3984 |
-
|
3985 |
-
//$h = $this->fonts[$this->currentFont]['FontBBox'][1];
|
3986 |
-
$h = $this->fonts[$this->currentFont]['Descender'];
|
3987 |
-
|
3988 |
-
return $size * $h / 1000;
|
3989 |
-
}
|
3990 |
-
|
3991 |
-
/**
|
3992 |
-
* filter the text, this is applied to all text just before being inserted into the pdf document
|
3993 |
-
* it escapes the various things that need to be escaped, and so on
|
3994 |
-
*
|
3995 |
-
* @access private
|
3996 |
-
*
|
3997 |
-
* @param $text
|
3998 |
-
* @param bool $bom
|
3999 |
-
* @param bool $convert_encoding
|
4000 |
-
* @return string
|
4001 |
-
*/
|
4002 |
-
function filterText($text, $bom = true, $convert_encoding = true)
|
4003 |
-
{
|
4004 |
-
if (!$this->numFonts) {
|
4005 |
-
$this->selectFont($this->defaultFont);
|
4006 |
-
}
|
4007 |
-
|
4008 |
-
if ($convert_encoding) {
|
4009 |
-
$cf = $this->currentFont;
|
4010 |
-
if (isset($this->fonts[$cf]) && $this->fonts[$cf]['isUnicode']) {
|
4011 |
-
$text = $this->utf8toUtf16BE($text, $bom);
|
4012 |
-
} else {
|
4013 |
-
//$text = html_entity_decode($text, ENT_QUOTES);
|
4014 |
-
$text = mb_convert_encoding($text, self::$targetEncoding, 'UTF-8');
|
4015 |
-
}
|
4016 |
-
} else if ($bom) {
|
4017 |
-
$text = $this->utf8toUtf16BE($text, $bom);
|
4018 |
-
}
|
4019 |
-
|
4020 |
-
// the chr(13) substitution fixes a bug seen in TCPDF (bug #1421290)
|
4021 |
-
return strtr($text, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
|
4022 |
-
}
|
4023 |
-
|
4024 |
-
/**
|
4025 |
-
* return array containing codepoints (UTF-8 character values) for the
|
4026 |
-
* string passed in.
|
4027 |
-
*
|
4028 |
-
* based on the excellent TCPDF code by Nicola Asuni and the
|
4029 |
-
* RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
|
4030 |
-
*
|
4031 |
-
* @access private
|
4032 |
-
* @author Orion Richardson
|
4033 |
-
* @since January 5, 2008
|
4034 |
-
*
|
4035 |
-
* @param string $text UTF-8 string to process
|
4036 |
-
*
|
4037 |
-
* @return array UTF-8 codepoints array for the string
|
4038 |
-
*/
|
4039 |
-
function utf8toCodePointsArray(&$text)
|
4040 |
-
{
|
4041 |
-
$length = mb_strlen($text, '8bit'); // http://www.php.net/manual/en/function.mb-strlen.php#77040
|
4042 |
-
$unicode = array(); // array containing unicode values
|
4043 |
-
$bytes = array(); // array containing single character byte sequences
|
4044 |
-
$numbytes = 1; // number of octets needed to represent the UTF-8 character
|
4045 |
-
|
4046 |
-
for ($i = 0; $i < $length; $i++) {
|
4047 |
-
$c = ord($text[$i]); // get one string character at time
|
4048 |
-
if (count($bytes) === 0) { // get starting octect
|
4049 |
-
if ($c <= 0x7F) {
|
4050 |
-
$unicode[] = $c; // use the character "as is" because is ASCII
|
4051 |
-
$numbytes = 1;
|
4052 |
-
} elseif (($c >> 0x05) === 0x06) { // 2 bytes character (0x06 = 110 BIN)
|
4053 |
-
$bytes[] = ($c - 0xC0) << 0x06;
|
4054 |
-
$numbytes = 2;
|
4055 |
-
} elseif (($c >> 0x04) === 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
|
4056 |
-
$bytes[] = ($c - 0xE0) << 0x0C;
|
4057 |
-
$numbytes = 3;
|
4058 |
-
} elseif (($c >> 0x03) === 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
|
4059 |
-
$bytes[] = ($c - 0xF0) << 0x12;
|
4060 |
-
$numbytes = 4;
|
4061 |
-
} else {
|
4062 |
-
// use replacement character for other invalid sequences
|
4063 |
-
$unicode[] = 0xFFFD;
|
4064 |
-
$bytes = array();
|
4065 |
-
$numbytes = 1;
|
4066 |
-
}
|
4067 |
-
} elseif (($c >> 0x06) === 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
|
4068 |
-
$bytes[] = $c - 0x80;
|
4069 |
-
if (count($bytes) === $numbytes) {
|
4070 |
-
// compose UTF-8 bytes to a single unicode value
|
4071 |
-
$c = $bytes[0];
|
4072 |
-
for ($j = 1; $j < $numbytes; $j++) {
|
4073 |
-
$c += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
|
4074 |
-
}
|
4075 |
-
if ((($c >= 0xD800) AND ($c <= 0xDFFF)) OR ($c >= 0x10FFFF)) {
|
4076 |
-
// The definition of UTF-8 prohibits encoding character numbers between
|
4077 |
-
// U+D800 and U+DFFF, which are reserved for use with the UTF-16
|
4078 |
-
// encoding form (as surrogate pairs) and do not directly represent
|
4079 |
-
// characters.
|
4080 |
-
$unicode[] = 0xFFFD; // use replacement character
|
4081 |
-
} else {
|
4082 |
-
$unicode[] = $c; // add char to array
|
4083 |
-
}
|
4084 |
-
// reset data for next char
|
4085 |
-
$bytes = array();
|
4086 |
-
$numbytes = 1;
|
4087 |
-
}
|
4088 |
-
} else {
|
4089 |
-
// use replacement character for other invalid sequences
|
4090 |
-
$unicode[] = 0xFFFD;
|
4091 |
-
$bytes = array();
|
4092 |
-
$numbytes = 1;
|
4093 |
-
}
|
4094 |
-
}
|
4095 |
-
|
4096 |
-
return $unicode;
|
4097 |
-
}
|
4098 |
-
|
4099 |
-
/**
|
4100 |
-
* convert UTF-8 to UTF-16 with an additional byte order marker
|
4101 |
-
* at the front if required.
|
4102 |
-
*
|
4103 |
-
* based on the excellent TCPDF code by Nicola Asuni and the
|
4104 |
-
* RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
|
4105 |
-
*
|
4106 |
-
* @access private
|
4107 |
-
* @author Orion Richardson
|
4108 |
-
* @since January 5, 2008
|
4109 |
-
*
|
4110 |
-
* @param string $text UTF-8 string to process
|
4111 |
-
* @param boolean $bom whether to add the byte order marker
|
4112 |
-
*
|
4113 |
-
* @return string UTF-16 result string
|
4114 |
-
*/
|
4115 |
-
function utf8toUtf16BE(&$text, $bom = true)
|
4116 |
-
{
|
4117 |
-
$out = $bom ? "\xFE\xFF" : '';
|
4118 |
-
|
4119 |
-
$unicode = $this->utf8toCodePointsArray($text);
|
4120 |
-
foreach ($unicode as $c) {
|
4121 |
-
if ($c === 0xFFFD) {
|
4122 |
-
$out .= "\xFF\xFD"; // replacement character
|
4123 |
-
} elseif ($c < 0x10000) {
|
4124 |
-
$out .= chr($c >> 0x08) . chr($c & 0xFF);
|
4125 |
-
} else {
|
4126 |
-
$c -= 0x10000;
|
4127 |
-
$w1 = 0xD800 | ($c >> 0x10);
|
4128 |
-
$w2 = 0xDC00 | ($c & 0x3FF);
|
4129 |
-
$out .= chr($w1 >> 0x08) . chr($w1 & 0xFF) . chr($w2 >> 0x08) . chr($w2 & 0xFF);
|
4130 |
-
}
|
4131 |
-
}
|
4132 |
-
|
4133 |
-
return $out;
|
4134 |
-
}
|
4135 |
-
|
4136 |
-
/**
|
4137 |
-
* given a start position and information about how text is to be laid out, calculate where
|
4138 |
-
* on the page the text will end
|
4139 |
-
*
|
4140 |
-
* @param $x
|
4141 |
-
* @param $y
|
4142 |
-
* @param $angle
|
4143 |
-
* @param $size
|
4144 |
-
* @param $wa
|
4145 |
-
* @param $text
|
4146 |
-
* @return array
|
4147 |
-
*/
|
4148 |
-
private function getTextPosition($x, $y, $angle, $size, $wa, $text)
|
4149 |
-
{
|
4150 |
-
// given this information return an array containing x and y for the end position as elements 0 and 1
|
4151 |
-
$w = $this->getTextWidth($size, $text);
|
4152 |
-
|
4153 |
-
// need to adjust for the number of spaces in this text
|
4154 |
-
$words = explode(' ', $text);
|
4155 |
-
$nspaces = count($words) - 1;
|
4156 |
-
$w += $wa * $nspaces;
|
4157 |
-
$a = deg2rad((float)$angle);
|
4158 |
-
|
4159 |
-
return array(cos($a) * $w + $x, -sin($a) * $w + $y);
|
4160 |
-
}
|
4161 |
-
|
4162 |
-
/**
|
4163 |
-
* Callback method used by smallCaps
|
4164 |
-
*
|
4165 |
-
* @param array $matches
|
4166 |
-
*
|
4167 |
-
* @return string
|
4168 |
-
*/
|
4169 |
-
function toUpper($matches)
|
4170 |
-
{
|
4171 |
-
return mb_strtoupper($matches[0]);
|
4172 |
-
}
|
4173 |
-
|
4174 |
-
function concatMatches($matches)
|
4175 |
-
{
|
4176 |
-
$str = "";
|
4177 |
-
foreach ($matches as $match) {
|
4178 |
-
$str .= $match[0];
|
4179 |
-
}
|
4180 |
-
|
4181 |
-
return $str;
|
4182 |
-
}
|
4183 |
-
|
4184 |
-
/**
|
4185 |
-
* register text for font subsetting
|
4186 |
-
*
|
4187 |
-
* @param $font
|
4188 |
-
* @param $text
|
4189 |
-
*/
|
4190 |
-
function registerText($font, $text)
|
4191 |
-
{
|
4192 |
-
if (!$this->isUnicode || in_array(mb_strtolower(basename($font)), self::$coreFonts)) {
|
4193 |
-
return;
|
4194 |
-
}
|
4195 |
-
|
4196 |
-
if (!isset($this->stringSubsets[$font])) {
|
4197 |
-
$this->stringSubsets[$font] = array();
|
4198 |
-
}
|
4199 |
-
|
4200 |
-
$this->stringSubsets[$font] = array_unique(
|
4201 |
-
array_merge($this->stringSubsets[$font], $this->utf8toCodePointsArray($text))
|
4202 |
-
);
|
4203 |
-
}
|
4204 |
-
|
4205 |
-
/**
|
4206 |
-
* add text to the document, at a specified location, size and angle on the page
|
4207 |
-
*
|
4208 |
-
* @param $x
|
4209 |
-
* @param $y
|
4210 |
-
* @param $size
|
4211 |
-
* @param $text
|
4212 |
-
* @param int $angle
|
4213 |
-
* @param int $wordSpaceAdjust
|
4214 |
-
* @param int $charSpaceAdjust
|
4215 |
-
* @param bool $smallCaps
|
4216 |
-
*/
|
4217 |
-
function addText($x, $y, $size, $text, $angle = 0, $wordSpaceAdjust = 0, $charSpaceAdjust = 0, $smallCaps = false)
|
4218 |
-
{
|
4219 |
-
if (!$this->numFonts) {
|
4220 |
-
$this->selectFont($this->defaultFont);
|
4221 |
-
}
|
4222 |
-
|
4223 |
-
$text = str_replace(array("\r", "\n"), "", $text);
|
4224 |
-
|
4225 |
-
if ($smallCaps) {
|
4226 |
-
preg_match_all("/(\P{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
|
4227 |
-
$lower = $this->concatMatches($matches);
|
4228 |
-
d($lower);
|
4229 |
-
|
4230 |
-
preg_match_all("/(\p{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
|
4231 |
-
$other = $this->concatMatches($matches);
|
4232 |
-
d($other);
|
4233 |
-
|
4234 |
-
//$text = preg_replace_callback("/\p{Ll}/u", array($this, "toUpper"), $text);
|
4235 |
-
}
|
4236 |
-
|
4237 |
-
// if there are any open callbacks, then they should be called, to show the start of the line
|
4238 |
-
if ($this->nCallback > 0) {
|
4239 |
-
for ($i = $this->nCallback; $i > 0; $i--) {
|
4240 |
-
// call each function
|
4241 |
-
$info = array(
|
4242 |
-
'x' => $x,
|
4243 |
-
'y' => $y,
|
4244 |
-
'angle' => $angle,
|
4245 |
-
'status' => 'sol',
|
4246 |
-
'p' => $this->callback[$i]['p'],
|
4247 |
-
'nCallback' => $this->callback[$i]['nCallback'],
|
4248 |
-
'height' => $this->callback[$i]['height'],
|
4249 |
-
'descender' => $this->callback[$i]['descender']
|
4250 |
-
);
|
4251 |
-
|
4252 |
-
$func = $this->callback[$i]['f'];
|
4253 |
-
$this->$func($info);
|
4254 |
-
}
|
4255 |
-
}
|
4256 |
-
|
4257 |
-
if ($angle == 0) {
|
4258 |
-
$this->addContent(sprintf("\nBT %.3F %.3F Td", $x, $y));
|
4259 |
-
} else {
|
4260 |
-
$a = deg2rad((float)$angle);
|
4261 |
-
$this->addContent(
|
4262 |
-
sprintf("\nBT %.3F %.3F %.3F %.3F %.3F %.3F Tm", cos($a), -sin($a), sin($a), cos($a), $x, $y)
|
4263 |
-
);
|
4264 |
-
}
|
4265 |
-
|
4266 |
-
if ($wordSpaceAdjust != 0) {
|
4267 |
-
$this->addContent(sprintf(" %.3F Tw", $wordSpaceAdjust));
|
4268 |
-
}
|
4269 |
-
|
4270 |
-
if ($charSpaceAdjust != 0) {
|
4271 |
-
$this->addContent(sprintf(" %.3F Tc", $charSpaceAdjust));
|
4272 |
-
}
|
4273 |
-
|
4274 |
-
$len = mb_strlen($text);
|
4275 |
-
$start = 0;
|
4276 |
-
|
4277 |
-
if ($start < $len) {
|
4278 |
-
$part = $text; // OAR - Don't need this anymore, given that $start always equals zero. substr($text, $start);
|
4279 |
-
$place_text = $this->filterText($part, false);
|
4280 |
-
// modify unicode text so that extra word spacing is manually implemented (bug #)
|
4281 |
-
$cf = $this->currentFont;
|
4282 |
-
if ($this->fonts[$cf]['isUnicode'] && $wordSpaceAdjust != 0) {
|
4283 |
-
$space_scale = 1000 / $size;
|
4284 |
-
$place_text = str_replace("\x00\x20", "\x00\x20)\x00\x20" . (-round($space_scale * $wordSpaceAdjust)) . "\x00\x20(", $place_text);
|
4285 |
-
}
|
4286 |
-
$this->addContent(" /F$this->currentFontNum " . sprintf('%.1F Tf ', $size));
|
4287 |
-
$this->addContent(" [($place_text)] TJ");
|
4288 |
-
}
|
4289 |
-
|
4290 |
-
if ($wordSpaceAdjust != 0) {
|
4291 |
-
$this->addContent(sprintf(" %.3F Tw", 0));
|
4292 |
-
}
|
4293 |
-
|
4294 |
-
if ($charSpaceAdjust != 0) {
|
4295 |
-
$this->addContent(sprintf(" %.3F Tc", 0));
|
4296 |
-
}
|
4297 |
-
|
4298 |
-
$this->addContent(' ET');
|
4299 |
-
|
4300 |
-
// if there are any open callbacks, then they should be called, to show the end of the line
|
4301 |
-
if ($this->nCallback > 0) {
|
4302 |
-
for ($i = $this->nCallback; $i > 0; $i--) {
|
4303 |
-
// call each function
|
4304 |
-
$tmp = $this->getTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, $text);
|
4305 |
-
$info = array(
|
4306 |
-
'x' => $tmp[0],
|
4307 |
-
'y' => $tmp[1],
|
4308 |
-
'angle' => $angle,
|
4309 |
-
'status' => 'eol',
|
4310 |
-
'p' => $this->callback[$i]['p'],
|
4311 |
-
'nCallback' => $this->callback[$i]['nCallback'],
|
4312 |
-
'height' => $this->callback[$i]['height'],
|
4313 |
-
'descender' => $this->callback[$i]['descender']
|
4314 |
-
);
|
4315 |
-
$func = $this->callback[$i]['f'];
|
4316 |
-
$this->$func($info);
|
4317 |
-
}
|
4318 |
-
}
|
4319 |
-
}
|
4320 |
-
|
4321 |
-
/**
|
4322 |
-
* calculate how wide a given text string will be on a page, at a given size.
|
4323 |
-
* this can be called externally, but is also used by the other class functions
|
4324 |
-
*
|
4325 |
-
* @param $size
|
4326 |
-
* @param $text
|
4327 |
-
* @param int $word_spacing
|
4328 |
-
* @param int $char_spacing
|
4329 |
-
* @return float|int
|
4330 |
-
*/
|
4331 |
-
function getTextWidth($size, $text, $word_spacing = 0, $char_spacing = 0)
|
4332 |
-
{
|
4333 |
-
static $ord_cache = array();
|
4334 |
-
|
4335 |
-
// this function should not change any of the settings, though it will need to
|
4336 |
-
// track any directives which change during calculation, so copy them at the start
|
4337 |
-
// and put them back at the end.
|
4338 |
-
$store_currentTextState = $this->currentTextState;
|
4339 |
-
|
4340 |
-
if (!$this->numFonts) {
|
4341 |
-
$this->selectFont($this->defaultFont);
|
4342 |
-
}
|
4343 |
-
|
4344 |
-
$text = str_replace(array("\r", "\n"), "", $text);
|
4345 |
-
|
4346 |
-
// converts a number or a float to a string so it can get the width
|
4347 |
-
$text = "$text";
|
4348 |
-
|
4349 |
-
// hmm, this is where it all starts to get tricky - use the font information to
|
4350 |
-
// calculate the width of each character, add them up and convert to user units
|
4351 |
-
$w = 0;
|
4352 |
-
$cf = $this->currentFont;
|
4353 |
-
$current_font = $this->fonts[$cf];
|
4354 |
-
$space_scale = 1000 / ($size > 0 ? $size : 1);
|
4355 |
-
$n_spaces = 0;
|
4356 |
-
|
4357 |
-
if ($current_font['isUnicode']) {
|
4358 |
-
// for Unicode, use the code points array to calculate width rather
|
4359 |
-
// than just the string itself
|
4360 |
-
$unicode = $this->utf8toCodePointsArray($text);
|
4361 |
-
|
4362 |
-
foreach ($unicode as $char) {
|
4363 |
-
// check if we have to replace character
|
4364 |
-
if (isset($current_font['differences'][$char])) {
|
4365 |
-
$char = $current_font['differences'][$char];
|
4366 |
-
}
|
4367 |
-
|
4368 |
-
if (isset($current_font['C'][$char])) {
|
4369 |
-
$char_width = $current_font['C'][$char];
|
4370 |
-
|
4371 |
-
// add the character width
|
4372 |
-
$w += $char_width;
|
4373 |
-
|
4374 |
-
// add additional padding for space
|
4375 |
-
if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space
|
4376 |
-
$w += $word_spacing * $space_scale;
|
4377 |
-
$n_spaces++;
|
4378 |
-
}
|
4379 |
-
}
|
4380 |
-
}
|
4381 |
-
|
4382 |
-
// add additional char spacing
|
4383 |
-
if ($char_spacing != 0) {
|
4384 |
-
$w += $char_spacing * $space_scale * (count($unicode) + $n_spaces);
|
4385 |
-
}
|
4386 |
-
|
4387 |
-
} else {
|
4388 |
-
// If CPDF is in Unicode mode but the current font does not support Unicode we need to convert the character set to Windows-1252
|
4389 |
-
if ($this->isUnicode) {
|
4390 |
-
$text = mb_convert_encoding($text, 'Windows-1252', 'UTF-8');
|
4391 |
-
}
|
4392 |
-
|
4393 |
-
$len = mb_strlen($text, 'Windows-1252');
|
4394 |
-
|
4395 |
-
for ($i = 0; $i < $len; $i++) {
|
4396 |
-
$c = $text[$i];
|
4397 |
-
$char = isset($ord_cache[$c]) ? $ord_cache[$c] : ($ord_cache[$c] = ord($c));
|
4398 |
-
|
4399 |
-
// check if we have to replace character
|
4400 |
-
if (isset($current_font['differences'][$char])) {
|
4401 |
-
$char = $current_font['differences'][$char];
|
4402 |
-
}
|
4403 |
-
|
4404 |
-
if (isset($current_font['C'][$char])) {
|
4405 |
-
$char_width = $current_font['C'][$char];
|
4406 |
-
|
4407 |
-
// add the character width
|
4408 |
-
$w += $char_width;
|
4409 |
-
|
4410 |
-
// add additional padding for space
|
4411 |
-
if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space
|
4412 |
-
$w += $word_spacing * $space_scale;
|
4413 |
-
$n_spaces++;
|
4414 |
-
}
|
4415 |
-
}
|
4416 |
-
}
|
4417 |
-
|
4418 |
-
// add additional char spacing
|
4419 |
-
if ($char_spacing != 0) {
|
4420 |
-
$w += $char_spacing * $space_scale * ($len + $n_spaces);
|
4421 |
-
}
|
4422 |
-
}
|
4423 |
-
|
4424 |
-
$this->currentTextState = $store_currentTextState;
|
4425 |
-
$this->setCurrentFont();
|
4426 |
-
|
4427 |
-
return $w * $size / 1000;
|
4428 |
-
}
|
4429 |
-
|
4430 |
-
/**
|
4431 |
-
* this will be called at a new page to return the state to what it was on the
|
4432 |
-
* end of the previous page, before the stack was closed down
|
4433 |
-
* This is to get around not being able to have open 'q' across pages
|
4434 |
-
*
|
4435 |
-
* @param int $pageEnd
|
4436 |
-
*/
|
4437 |
-
function saveState($pageEnd = 0)
|
4438 |
-
{
|
4439 |
-
if ($pageEnd) {
|
4440 |
-
// this will be called at a new page to return the state to what it was on the
|
4441 |
-
// end of the previous page, before the stack was closed down
|
4442 |
-
// This is to get around not being able to have open 'q' across pages
|
4443 |
-
$opt = $this->stateStack[$pageEnd];
|
4444 |
-
// ok to use this as stack starts numbering at 1
|
4445 |
-
$this->setColor($opt['col'], true);
|
4446 |
-
$this->setStrokeColor($opt['str'], true);
|
4447 |
-
$this->addContent("\n" . $opt['lin']);
|
4448 |
-
// $this->currentLineStyle = $opt['lin'];
|
4449 |
-
} else {
|
4450 |
-
$this->nStateStack++;
|
4451 |
-
$this->stateStack[$this->nStateStack] = array(
|
4452 |
-
'col' => $this->currentColor,
|
4453 |
-
'str' => $this->currentStrokeColor,
|
4454 |
-
'lin' => $this->currentLineStyle
|
4455 |
-
);
|
4456 |
-
}
|
4457 |
-
|
4458 |
-
$this->save();
|
4459 |
-
}
|
4460 |
-
|
4461 |
-
/**
|
4462 |
-
* restore a previously saved state
|
4463 |
-
*
|
4464 |
-
* @param int $pageEnd
|
4465 |
-
*/
|
4466 |
-
function restoreState($pageEnd = 0)
|
4467 |
-
{
|
4468 |
-
if (!$pageEnd) {
|
4469 |
-
$n = $this->nStateStack;
|
4470 |
-
$this->currentColor = $this->stateStack[$n]['col'];
|
4471 |
-
$this->currentStrokeColor = $this->stateStack[$n]['str'];
|
4472 |
-
$this->addContent("\n" . $this->stateStack[$n]['lin']);
|
4473 |
-
$this->currentLineStyle = $this->stateStack[$n]['lin'];
|
4474 |
-
$this->stateStack[$n] = null;
|
4475 |
-
unset($this->stateStack[$n]);
|
4476 |
-
$this->nStateStack--;
|
4477 |
-
}
|
4478 |
-
|
4479 |
-
$this->restore();
|
4480 |
-
}
|
4481 |
-
|
4482 |
-
/**
|
4483 |
-
* make a loose object, the output will go into this object, until it is closed, then will revert to
|
4484 |
-
* the current one.
|
4485 |
-
* this object will not appear until it is included within a page.
|
4486 |
-
* the function will return the object number
|
4487 |
-
*
|
4488 |
-
* @return int
|
4489 |
-
*/
|
4490 |
-
function openObject()
|
4491 |
-
{
|
4492 |
-
$this->nStack++;
|
4493 |
-
$this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
|
4494 |
-
// add a new object of the content type, to hold the data flow
|
4495 |
-
$this->numObj++;
|
4496 |
-
$this->o_contents($this->numObj, 'new');
|
4497 |
-
$this->currentContents = $this->numObj;
|
4498 |
-
$this->looseObjects[$this->numObj] = 1;
|
4499 |
-
|
4500 |
-
return $this->numObj;
|
4501 |
-
}
|
4502 |
-
|
4503 |
-
/**
|
4504 |
-
* open an existing object for editing
|
4505 |
-
*
|
4506 |
-
* @param $id
|
4507 |
-
*/
|
4508 |
-
function reopenObject($id)
|
4509 |
-
{
|
4510 |
-
$this->nStack++;
|
4511 |
-
$this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
|
4512 |
-
$this->currentContents = $id;
|
4513 |
-
|
4514 |
-
// also if this object is the primary contents for a page, then set the current page to its parent
|
4515 |
-
if (isset($this->objects[$id]['onPage'])) {
|
4516 |
-
$this->currentPage = $this->objects[$id]['onPage'];
|
4517 |
-
}
|
4518 |
-
}
|
4519 |
-
|
4520 |
-
/**
|
4521 |
-
* close an object
|
4522 |
-
*/
|
4523 |
-
function closeObject()
|
4524 |
-
{
|
4525 |
-
// close the object, as long as there was one open in the first place, which will be indicated by
|
4526 |
-
// an objectId on the stack.
|
4527 |
-
if ($this->nStack > 0) {
|
4528 |
-
$this->currentContents = $this->stack[$this->nStack]['c'];
|
4529 |
-
$this->currentPage = $this->stack[$this->nStack]['p'];
|
4530 |
-
$this->nStack--;
|
4531 |
-
// easier to probably not worry about removing the old entries, they will be overwritten
|
4532 |
-
// if there are new ones.
|
4533 |
-
}
|
4534 |
-
}
|
4535 |
-
|
4536 |
-
/**
|
4537 |
-
* stop an object from appearing on pages from this point on
|
4538 |
-
*
|
4539 |
-
* @param $id
|
4540 |
-
*/
|
4541 |
-
function stopObject($id)
|
4542 |
-
{
|
4543 |
-
// if an object has been appearing on pages up to now, then stop it, this page will
|
4544 |
-
// be the last one that could contain it.
|
4545 |
-
if (isset($this->addLooseObjects[$id])) {
|
4546 |
-
$this->addLooseObjects[$id] = '';
|
4547 |
-
}
|
4548 |
-
}
|
4549 |
-
|
4550 |
-
/**
|
4551 |
-
* after an object has been created, it wil only show if it has been added, using this function.
|
4552 |
-
*
|
4553 |
-
* @param $id
|
4554 |
-
* @param string $options
|
4555 |
-
*/
|
4556 |
-
function addObject($id, $options = 'add')
|
4557 |
-
{
|
4558 |
-
// add the specified object to the page
|
4559 |
-
if (isset($this->looseObjects[$id]) && $this->currentContents != $id) {
|
4560 |
-
// then it is a valid object, and it is not being added to itself
|
4561 |
-
switch ($options) {
|
4562 |
-
case 'all':
|
4563 |
-
// then this object is to be added to this page (done in the next block) and
|
4564 |
-
// all future new pages.
|
4565 |
-
$this->addLooseObjects[$id] = 'all';
|
4566 |
-
|
4567 |
-
case 'add':
|
4568 |
-
if (isset($this->objects[$this->currentContents]['onPage'])) {
|
4569 |
-
// then the destination contents is the primary for the page
|
4570 |
-
// (though this object is actually added to that page)
|
4571 |
-
$this->o_page($this->objects[$this->currentContents]['onPage'], 'content', $id);
|
4572 |
-
}
|
4573 |
-
break;
|
4574 |
-
|
4575 |
-
case 'even':
|
4576 |
-
$this->addLooseObjects[$id] = 'even';
|
4577 |
-
$pageObjectId = $this->objects[$this->currentContents]['onPage'];
|
4578 |
-
if ($this->objects[$pageObjectId]['info']['pageNum'] % 2 == 0) {
|
4579 |
-
$this->addObject($id);
|
4580 |
-
// hacky huh :)
|
4581 |
-
}
|
4582 |
-
break;
|
4583 |
-
|
4584 |
-
case 'odd':
|
4585 |
-
$this->addLooseObjects[$id] = 'odd';
|
4586 |
-
$pageObjectId = $this->objects[$this->currentContents]['onPage'];
|
4587 |
-
if ($this->objects[$pageObjectId]['info']['pageNum'] % 2 == 1) {
|
4588 |
-
$this->addObject($id);
|
4589 |
-
// hacky huh :)
|
4590 |
-
}
|
4591 |
-
break;
|
4592 |
-
|
4593 |
-
case 'next':
|
4594 |
-
$this->addLooseObjects[$id] = 'all';
|
4595 |
-
break;
|
4596 |
-
|
4597 |
-
case 'nexteven':
|
4598 |
-
$this->addLooseObjects[$id] = 'even';
|
4599 |
-
break;
|
4600 |
-
|
4601 |
-
case 'nextodd':
|
4602 |
-
$this->addLooseObjects[$id] = 'odd';
|
4603 |
-
break;
|
4604 |
-
}
|
4605 |
-
}
|
4606 |
-
}
|
4607 |
-
|
4608 |
-
/**
|
4609 |
-
* return a storable representation of a specific object
|
4610 |
-
*
|
4611 |
-
* @param $id
|
4612 |
-
* @return string|null
|
4613 |
-
*/
|
4614 |
-
function serializeObject($id)
|
4615 |
-
{
|
4616 |
-
if (array_key_exists($id, $this->objects)) {
|
4617 |
-
return serialize($this->objects[$id]);
|
4618 |
-
}
|
4619 |
-
|
4620 |
-
return null;
|
4621 |
-
}
|
4622 |
-
|
4623 |
-
/**
|
4624 |
-
* restore an object from its stored representation. returns its new object id.
|
4625 |
-
*
|
4626 |
-
* @param $obj
|
4627 |
-
* @return int
|
4628 |
-
*/
|
4629 |
-
function restoreSerializedObject($obj)
|
4630 |
-
{
|
4631 |
-
$obj_id = $this->openObject();
|
4632 |
-
$this->objects[$obj_id] = unserialize($obj);
|
4633 |
-
$this->closeObject();
|
4634 |
-
|
4635 |
-
return $obj_id;
|
4636 |
-
}
|
4637 |
-
|
4638 |
-
/**
|
4639 |
-
* add content to the documents info object
|
4640 |
-
*
|
4641 |
-
* @param $label
|
4642 |
-
* @param int $value
|
4643 |
-
*/
|
4644 |
-
function addInfo($label, $value = 0)
|
4645 |
-
{
|
4646 |
-
// this will only work if the label is one of the valid ones.
|
4647 |
-
// modify this so that arrays can be passed as well.
|
4648 |
-
// if $label is an array then assume that it is key => value pairs
|
4649 |
-
// else assume that they are both scalar, anything else will probably error
|
4650 |
-
if (is_array($label)) {
|
4651 |
-
foreach ($label as $l => $v) {
|
4652 |
-
$this->o_info($this->infoObject, $l, $v);
|
4653 |
-
}
|
4654 |
-
} else {
|
4655 |
-
$this->o_info($this->infoObject, $label, $value);
|
4656 |
-
}
|
4657 |
-
}
|
4658 |
-
|
4659 |
-
/**
|
4660 |
-
* set the viewer preferences of the document, it is up to the browser to obey these.
|
4661 |
-
*
|
4662 |
-
* @param $label
|
4663 |
-
* @param int $value
|
4664 |
-
*/
|
4665 |
-
function setPreferences($label, $value = 0)
|
4666 |
-
{
|
4667 |
-
// this will only work if the label is one of the valid ones.
|
4668 |
-
if (is_array($label)) {
|
4669 |
-
foreach ($label as $l => $v) {
|
4670 |
-
$this->o_catalog($this->catalogId, 'viewerPreferences', array($l => $v));
|
4671 |
-
}
|
4672 |
-
} else {
|
4673 |
-
$this->o_catalog($this->catalogId, 'viewerPreferences', array($label => $value));
|
4674 |
-
}
|
4675 |
-
}
|
4676 |
-
|
4677 |
-
/**
|
4678 |
-
* extract an integer from a position in a byte stream
|
4679 |
-
*
|
4680 |
-
* @param $data
|
4681 |
-
* @param $pos
|
4682 |
-
* @param $num
|
4683 |
-
* @return int
|
4684 |
-
*/
|
4685 |
-
private function getBytes(&$data, $pos, $num)
|
4686 |
-
{
|
4687 |
-
// return the integer represented by $num bytes from $pos within $data
|
4688 |
-
$ret = 0;
|
4689 |
-
for ($i = 0; $i < $num; $i++) {
|
4690 |
-
$ret *= 256;
|
4691 |
-
$ret += ord($data[$pos + $i]);
|
4692 |
-
}
|
4693 |
-
|
4694 |
-
return $ret;
|
4695 |
-
}
|
4696 |
-
|
4697 |
-
/**
|
4698 |
-
* Check if image already added to pdf image directory.
|
4699 |
-
* If yes, need not to create again (pass empty data)
|
4700 |
-
*
|
4701 |
-
* @param $imgname
|
4702 |
-
* @return bool
|
4703 |
-
*/
|
4704 |
-
function image_iscached($imgname)
|
4705 |
-
{
|
4706 |
-
return isset($this->imagelist[$imgname]);
|
4707 |
-
}
|
4708 |
-
|
4709 |
-
/**
|
4710 |
-
* add a PNG image into the document, from a GD object
|
4711 |
-
* this should work with remote files
|
4712 |
-
*
|
4713 |
-
* @param string $file The PNG file
|
4714 |
-
* @param float $x X position
|
4715 |
-
* @param float $y Y position
|
4716 |
-
* @param float $w Width
|
4717 |
-
* @param float $h Height
|
4718 |
-
* @param resource $img A GD resource
|
4719 |
-
* @param bool $is_mask true if the image is a mask
|
4720 |
-
* @param bool $mask true if the image is masked
|
4721 |
-
* @throws Exception
|
4722 |
-
*/
|
4723 |
-
function addImagePng($file, $x, $y, $w = 0.0, $h = 0.0, &$img, $is_mask = false, $mask = null)
|
4724 |
-
{
|
4725 |
-
if (!function_exists("imagepng")) {
|
4726 |
-
throw new \Exception("The PHP GD extension is required, but is not installed.");
|
4727 |
-
}
|
4728 |
-
|
4729 |
-
//if already cached, need not to read again
|
4730 |
-
if (isset($this->imagelist[$file])) {
|
4731 |
-
$data = null;
|
4732 |
-
} else {
|
4733 |
-
// Example for transparency handling on new image. Retain for current image
|
4734 |
-
// $tIndex = imagecolortransparent($img);
|
4735 |
-
// if ($tIndex > 0) {
|
4736 |
-
// $tColor = imagecolorsforindex($img, $tIndex);
|
4737 |
-
// $new_tIndex = imagecolorallocate($new_img, $tColor['red'], $tColor['green'], $tColor['blue']);
|
4738 |
-
// imagefill($new_img, 0, 0, $new_tIndex);
|
4739 |
-
// imagecolortransparent($new_img, $new_tIndex);
|
4740 |
-
// }
|
4741 |
-
// blending mode (literal/blending) on drawing into current image. not relevant when not saved or not drawn
|
4742 |
-
//imagealphablending($img, true);
|
4743 |
-
|
4744 |
-
//default, but explicitely set to ensure pdf compatibility
|
4745 |
-
imagesavealpha($img, false/*!$is_mask && !$mask*/);
|
4746 |
-
|
4747 |
-
$error = 0;
|
4748 |
-
//DEBUG_IMG_TEMP
|
4749 |
-
//debugpng
|
4750 |
-
if (defined("DEBUGPNG") && DEBUGPNG) {
|
4751 |
-
print '[addImagePng ' . $file . ']';
|
4752 |
-
}
|
4753 |
-
|
4754 |
-
ob_start();
|
4755 |
-
@imagepng($img);
|
4756 |
-
$data = ob_get_clean();
|
4757 |
-
|
4758 |
-
if ($data == '') {
|
4759 |
-
$error = 1;
|
4760 |
-
$errormsg = 'trouble writing file from GD';
|
4761 |
-
//DEBUG_IMG_TEMP
|
4762 |
-
//debugpng
|
4763 |
-
if (defined("DEBUGPNG") && DEBUGPNG) {
|
4764 |
-
print 'trouble writing file from GD';
|
4765 |
-
}
|
4766 |
-
}
|
4767 |
-
|
4768 |
-
if ($error) {
|
4769 |
-
$this->addMessage('PNG error - (' . $file . ') ' . $errormsg);
|
4770 |
-
|
4771 |
-
return;
|
4772 |
-
}
|
4773 |
-
} //End isset($this->imagelist[$file]) (png Duplicate removal)
|
4774 |
-
|
4775 |
-
$this->addPngFromBuf($file, $x, $y, $w, $h, $data, $is_mask, $mask);
|
4776 |
-
}
|
4777 |
-
|
4778 |
-
/**
|
4779 |
-
* @param $file
|
4780 |
-
* @param $x
|
4781 |
-
* @param $y
|
4782 |
-
* @param $w
|
4783 |
-
* @param $h
|
4784 |
-
* @param $byte
|
4785 |
-
*/
|
4786 |
-
protected function addImagePngAlpha($file, $x, $y, $w, $h, $byte)
|
4787 |
-
{
|
4788 |
-
// generate images
|
4789 |
-
$img = imagecreatefrompng($file);
|
4790 |
-
|
4791 |
-
if ($img === false) {
|
4792 |
-
return;
|
4793 |
-
}
|
4794 |
-
|
4795 |
-
// FIXME The pixel transformation doesn't work well with 8bit PNGs
|
4796 |
-
$eight_bit = ($byte & 4) !== 4;
|
4797 |
-
|
4798 |
-
$wpx = imagesx($img);
|
4799 |
-
$hpx = imagesy($img);
|
4800 |
-
|
4801 |
-
imagesavealpha($img, false);
|
4802 |
-
|
4803 |
-
// create temp alpha file
|
4804 |
-
$tempfile_alpha = tempnam($this->tmp, "cpdf_img_");
|
4805 |
-
@unlink($tempfile_alpha);
|
4806 |
-
$tempfile_alpha = "$tempfile_alpha.png";
|
4807 |
-
|
4808 |
-
// create temp plain file
|
4809 |
-
$tempfile_plain = tempnam($this->tmp, "cpdf_img_");
|
4810 |
-
@unlink($tempfile_plain);
|
4811 |
-
$tempfile_plain = "$tempfile_plain.png";
|
4812 |
-
|
4813 |
-
$imgalpha = imagecreate($wpx, $hpx);
|
4814 |
-
imagesavealpha($imgalpha, false);
|
4815 |
-
|
4816 |
-
// generate gray scale palette (0 -> 255)
|
4817 |
-
for ($c = 0; $c < 256; ++$c) {
|
4818 |
-
imagecolorallocate($imgalpha, $c, $c, $c);
|
4819 |
-
}
|
4820 |
-
|
4821 |
-
// Use PECL gmagick + Graphics Magic to process transparent PNG images
|
4822 |
-
if (extension_loaded("gmagick")) {
|
4823 |
-
$gmagick = new \Gmagick($file);
|
4824 |
-
$gmagick->setimageformat('png');
|
4825 |
-
|
4826 |
-
// Get opacity channel (negative of alpha channel)
|
4827 |
-
$alpha_channel_neg = clone $gmagick;
|
4828 |
-
$alpha_channel_neg->separateimagechannel(\Gmagick::CHANNEL_OPACITY);
|
4829 |
-
|
4830 |
-
// Negate opacity channel
|
4831 |
-
$alpha_channel = new \Gmagick();
|
4832 |
-
$alpha_channel->newimage($wpx, $hpx, "#FFFFFF", "png");
|
4833 |
-
$alpha_channel->compositeimage($alpha_channel_neg, \Gmagick::COMPOSITE_DIFFERENCE, 0, 0);
|
4834 |
-
$alpha_channel->separateimagechannel(\Gmagick::CHANNEL_RED);
|
4835 |
-
$alpha_channel->writeimage($tempfile_alpha);
|
4836 |
-
|
4837 |
-
// Cast to 8bit+palette
|
4838 |
-
$imgalpha_ = imagecreatefrompng($tempfile_alpha);
|
4839 |
-
imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx);
|
4840 |
-
imagedestroy($imgalpha_);
|
4841 |
-
imagepng($imgalpha, $tempfile_alpha);
|
4842 |
-
|
4843 |
-
// Make opaque image
|
4844 |
-
$color_channels = new \Gmagick();
|
4845 |
-
$color_channels->newimage($wpx, $hpx, "#FFFFFF", "png");
|
4846 |
-
$color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYRED, 0, 0);
|
4847 |
-
$color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYGREEN, 0, 0);
|
4848 |
-
$color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYBLUE, 0, 0);
|
4849 |
-
$color_channels->writeimage($tempfile_plain);
|
4850 |
-
|
4851 |
-
$imgplain = imagecreatefrompng($tempfile_plain);
|
4852 |
-
}
|
4853 |
-
// Use PECL imagick + ImageMagic to process transparent PNG images
|
4854 |
-
elseif (extension_loaded("imagick")) {
|
4855 |
-
// Native cloning was added to pecl-imagick in svn commit 263814
|
4856 |
-
// the first version containing it was 3.0.1RC1
|
4857 |
-
static $imagickClonable = null;
|
4858 |
-
if ($imagickClonable === null) {
|
4859 |
-
$imagickClonable = version_compare(Imagick::IMAGICK_EXTVER, '3.0.1rc1') > 0;
|
4860 |
-
}
|
4861 |
-
|
4862 |
-
$imagick = new \Imagick($file);
|
4863 |
-
$imagick->setFormat('png');
|
4864 |
-
|
4865 |
-
// Get opacity channel (negative of alpha channel)
|
4866 |
-
if ($imagick->getImageAlphaChannel() !== 0) {
|
4867 |
-
$alpha_channel = $imagickClonable ? clone $imagick : $imagick->clone();
|
4868 |
-
$alpha_channel->separateImageChannel(\Imagick::CHANNEL_ALPHA);
|
4869 |
-
$alpha_channel->negateImage(true);
|
4870 |
-
$alpha_channel->writeImage($tempfile_alpha);
|
4871 |
-
|
4872 |
-
// Cast to 8bit+palette
|
4873 |
-
$imgalpha_ = imagecreatefrompng($tempfile_alpha);
|
4874 |
-
imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx);
|
4875 |
-
imagedestroy($imgalpha_);
|
4876 |
-
imagepng($imgalpha, $tempfile_alpha);
|
4877 |
-
} else {
|
4878 |
-
$tempfile_alpha = null;
|
4879 |
-
}
|
4880 |
-
|
4881 |
-
// Make opaque image
|
4882 |
-
$color_channels = new \Imagick();
|
4883 |
-
$color_channels->newImage($wpx, $hpx, "#FFFFFF", "png");
|
4884 |
-
$color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYRED, 0, 0);
|
4885 |
-
$color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYGREEN, 0, 0);
|
4886 |
-
$color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYBLUE, 0, 0);
|
4887 |
-
$color_channels->writeImage($tempfile_plain);
|
4888 |
-
|
4889 |
-
$imgplain = imagecreatefrompng($tempfile_plain);
|
4890 |
-
} else {
|
4891 |
-
// allocated colors cache
|
4892 |
-
$allocated_colors = array();
|
4893 |
-
|
4894 |
-
// extract alpha channel
|
4895 |
-
for ($xpx = 0; $xpx < $wpx; ++$xpx) {
|
4896 |
-
for ($ypx = 0; $ypx < $hpx; ++$ypx) {
|
4897 |
-
$color = imagecolorat($img, $xpx, $ypx);
|
4898 |
-
$col = imagecolorsforindex($img, $color);
|
4899 |
-
$alpha = $col['alpha'];
|
4900 |
-
|
4901 |
-
if ($eight_bit) {
|
4902 |
-
// with gamma correction
|
4903 |
-
$gammacorr = 2.2;
|
4904 |
-
$pixel = pow((((127 - $alpha) * 255 / 127) / 255), $gammacorr) * 255;
|
4905 |
-
} else {
|
4906 |
-
// without gamma correction
|
4907 |
-
$pixel = (127 - $alpha) * 2;
|
4908 |
-
|
4909 |
-
$key = $col['red'] . $col['green'] . $col['blue'];
|
4910 |
-
|
4911 |
-
if (!isset($allocated_colors[$key])) {
|
4912 |
-
$pixel_img = imagecolorallocate($img, $col['red'], $col['green'], $col['blue']);
|
4913 |
-
$allocated_colors[$key] = $pixel_img;
|
4914 |
-
} else {
|
4915 |
-
$pixel_img = $allocated_colors[$key];
|
4916 |
-
}
|
4917 |
-
|
4918 |
-
imagesetpixel($img, $xpx, $ypx, $pixel_img);
|
4919 |
-
}
|
4920 |
-
|
4921 |
-
imagesetpixel($imgalpha, $xpx, $ypx, $pixel);
|
4922 |
-
}
|
4923 |
-
}
|
4924 |
-
|
4925 |
-
// extract image without alpha channel
|
4926 |
-
$imgplain = imagecreatetruecolor($wpx, $hpx);
|
4927 |
-
imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
|
4928 |
-
imagedestroy($img);
|
4929 |
-
|
4930 |
-
imagepng($imgalpha, $tempfile_alpha);
|
4931 |
-
imagepng($imgplain, $tempfile_plain);
|
4932 |
-
}
|
4933 |
-
|
4934 |
-
// embed mask image
|
4935 |
-
if ($tempfile_alpha) {
|
4936 |
-
$this->addImagePng($tempfile_alpha, $x, $y, $w, $h, $imgalpha, true);
|
4937 |
-
imagedestroy($imgalpha);
|
4938 |
-
}
|
4939 |
-
|
4940 |
-
// embed image, masked with previously embedded mask
|
4941 |
-
$this->addImagePng($tempfile_plain, $x, $y, $w, $h, $imgplain, false, ($tempfile_alpha !== null));
|
4942 |
-
imagedestroy($imgplain);
|
4943 |
-
|
4944 |
-
// remove temp files
|
4945 |
-
if ($tempfile_alpha) {
|
4946 |
-
unlink($tempfile_alpha);
|
4947 |
-
}
|
4948 |
-
unlink($tempfile_plain);
|
4949 |
-
}
|
4950 |
-
|
4951 |
-
/**
|
4952 |
-
* add a PNG image into the document, from a file
|
4953 |
-
* this should work with remote files
|
4954 |
-
*
|
4955 |
-
* @param $file
|
4956 |
-
* @param $x
|
4957 |
-
* @param $y
|
4958 |
-
* @param int $w
|
4959 |
-
* @param int $h
|
4960 |
-
* @throws Exception
|
4961 |
-
*/
|
4962 |
-
function addPngFromFile($file, $x, $y, $w = 0, $h = 0)
|
4963 |
-
{
|
4964 |
-
if (!function_exists("imagecreatefrompng")) {
|
4965 |
-
throw new \Exception("The PHP GD extension is required, but is not installed.");
|
4966 |
-
}
|
4967 |
-
|
4968 |
-
//if already cached, need not to read again
|
4969 |
-
if (isset($this->imagelist[$file])) {
|
4970 |
-
$img = null;
|
4971 |
-
} else {
|
4972 |
-
$info = file_get_contents($file, false, null, 24, 5);
|
4973 |
-
$meta = unpack("CbitDepth/CcolorType/CcompressionMethod/CfilterMethod/CinterlaceMethod", $info);
|
4974 |
-
$bit_depth = $meta["bitDepth"];
|
4975 |
-
$color_type = $meta["colorType"];
|
4976 |
-
|
4977 |
-
// http://www.w3.org/TR/PNG/#11IHDR
|
4978 |
-
// 3 => indexed
|
4979 |
-
// 4 => greyscale with alpha
|
4980 |
-
// 6 => fullcolor with alpha
|
4981 |
-
$is_alpha = in_array($color_type, array(4, 6)) || ($color_type == 3 && $bit_depth != 4);
|
4982 |
-
|
4983 |
-
if ($is_alpha) { // exclude grayscale alpha
|
4984 |
-
$this->addImagePngAlpha($file, $x, $y, $w, $h, $color_type);
|
4985 |
-
return;
|
4986 |
-
}
|
4987 |
-
|
4988 |
-
//png files typically contain an alpha channel.
|
4989 |
-
//pdf file format or class.pdf does not support alpha blending.
|
4990 |
-
//on alpha blended images, more transparent areas have a color near black.
|
4991 |
-
//This appears in the result on not storing the alpha channel.
|
4992 |
-
//Correct would be the box background image or its parent when transparent.
|
4993 |
-
//But this would make the image dependent on the background.
|
4994 |
-
//Therefore create an image with white background and copy in
|
4995 |
-
//A more natural background than black is white.
|
4996 |
-
//Therefore create an empty image with white background and merge the
|
4997 |
-
//image in with alpha blending.
|
4998 |
-
$imgtmp = @imagecreatefrompng($file);
|
4999 |
-
if (!$imgtmp) {
|
5000 |
-
return;
|
5001 |
-
}
|
5002 |
-
$sx = imagesx($imgtmp);
|
5003 |
-
$sy = imagesy($imgtmp);
|
5004 |
-
$img = imagecreatetruecolor($sx, $sy);
|
5005 |
-
imagealphablending($img, true);
|
5006 |
-
|
5007 |
-
// @todo is it still needed ??
|
5008 |
-
$ti = imagecolortransparent($imgtmp);
|
5009 |
-
if ($ti >= 0) {
|
5010 |
-
$tc = imagecolorsforindex($imgtmp, $ti);
|
5011 |
-
$ti = imagecolorallocate($img, $tc['red'], $tc['green'], $tc['blue']);
|
5012 |
-
imagefill($img, 0, 0, $ti);
|
5013 |
-
imagecolortransparent($img, $ti);
|
5014 |
-
} else {
|
5015 |
-
imagefill($img, 1, 1, imagecolorallocate($img, 255, 255, 255));
|
5016 |
-
}
|
5017 |
-
|
5018 |
-
imagecopy($img, $imgtmp, 0, 0, 0, 0, $sx, $sy);
|
5019 |
-
imagedestroy($imgtmp);
|
5020 |
-
}
|
5021 |
-
$this->addImagePng($file, $x, $y, $w, $h, $img);
|
5022 |
-
|
5023 |
-
if ($img) {
|
5024 |
-
imagedestroy($img);
|
5025 |
-
}
|
5026 |
-
}
|
5027 |
-
|
5028 |
-
/**
|
5029 |
-
* add a PNG image into the document, from a file
|
5030 |
-
* this should work with remote files
|
5031 |
-
*
|
5032 |
-
* @param $file
|
5033 |
-
* @param $x
|
5034 |
-
* @param $y
|
5035 |
-
* @param int $w
|
5036 |
-
* @param int $h
|
5037 |
-
*/
|
5038 |
-
function addSvgFromFile($file, $x, $y, $w = 0, $h = 0)
|
5039 |
-
{
|
5040 |
-
$doc = new \Svg\Document();
|
5041 |
-
$doc->loadFile($file);
|
5042 |
-
$dimensions = $doc->getDimensions();
|
5043 |
-
|
5044 |
-
$this->save();
|
5045 |
-
|
5046 |
-
$this->transform(array($w / $dimensions["width"], 0, 0, $h / $dimensions["height"], $x, $y));
|
5047 |
-
|
5048 |
-
$surface = new \Svg\Surface\SurfaceCpdf($doc, $this);
|
5049 |
-
$doc->render($surface);
|
5050 |
-
|
5051 |
-
$this->restore();
|
5052 |
-
}
|
5053 |
-
|
5054 |
-
/**
|
5055 |
-
* add a PNG image into the document, from a memory buffer of the file
|
5056 |
-
*
|
5057 |
-
* @param $file
|
5058 |
-
* @param $x
|
5059 |
-
* @param $y
|
5060 |
-
* @param float $w
|
5061 |
-
* @param float $h
|
5062 |
-
* @param $data
|
5063 |
-
* @param bool $is_mask
|
5064 |
-
* @param null $mask
|
5065 |
-
*/
|
5066 |
-
function addPngFromBuf($file, $x, $y, $w = 0.0, $h = 0.0, &$data, $is_mask = false, $mask = null)
|
5067 |
-
{
|
5068 |
-
if (isset($this->imagelist[$file])) {
|
5069 |
-
$data = null;
|
5070 |
-
$info['width'] = $this->imagelist[$file]['w'];
|
5071 |
-
$info['height'] = $this->imagelist[$file]['h'];
|
5072 |
-
$label = $this->imagelist[$file]['label'];
|
5073 |
-
} else {
|
5074 |
-
if ($data == null) {
|
5075 |
-
$this->addMessage('addPngFromBuf error - data not present!');
|
5076 |
-
|
5077 |
-
return;
|
5078 |
-
}
|
5079 |
-
|
5080 |
-
$error = 0;
|
5081 |
-
|
5082 |
-
if (!$error) {
|
5083 |
-
$header = chr(137) . chr(80) . chr(78) . chr(71) . chr(13) . chr(10) . chr(26) . chr(10);
|
5084 |
-
|
5085 |
-
if (mb_substr($data, 0, 8, '8bit') != $header) {
|
5086 |
-
$error = 1;
|
5087 |
-
|
5088 |
-
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5089 |
-
print '[addPngFromFile this file does not have a valid header ' . $file . ']';
|
5090 |
-
}
|
5091 |
-
|
5092 |
-
$errormsg = 'this file does not have a valid header';
|
5093 |
-
}
|
5094 |
-
}
|
5095 |
-
|
5096 |
-
if (!$error) {
|
5097 |
-
// set pointer
|
5098 |
-
$p = 8;
|
5099 |
-
$len = mb_strlen($data, '8bit');
|
5100 |
-
|
5101 |
-
// cycle through the file, identifying chunks
|
5102 |
-
$haveHeader = 0;
|
5103 |
-
$info = array();
|
5104 |
-
$idata = '';
|
5105 |
-
$pdata = '';
|
5106 |
-
|
5107 |
-
while ($p < $len) {
|
5108 |
-
$chunkLen = $this->getBytes($data, $p, 4);
|
5109 |
-
$chunkType = mb_substr($data, $p + 4, 4, '8bit');
|
5110 |
-
|
5111 |
-
switch ($chunkType) {
|
5112 |
-
case 'IHDR':
|
5113 |
-
// this is where all the file information comes from
|
5114 |
-
$info['width'] = $this->getBytes($data, $p + 8, 4);
|
5115 |
-
$info['height'] = $this->getBytes($data, $p + 12, 4);
|
5116 |
-
$info['bitDepth'] = ord($data[$p + 16]);
|
5117 |
-
$info['colorType'] = ord($data[$p + 17]);
|
5118 |
-
$info['compressionMethod'] = ord($data[$p + 18]);
|
5119 |
-
$info['filterMethod'] = ord($data[$p + 19]);
|
5120 |
-
$info['interlaceMethod'] = ord($data[$p + 20]);
|
5121 |
-
|
5122 |
-
//print_r($info);
|
5123 |
-
$haveHeader = 1;
|
5124 |
-
if ($info['compressionMethod'] != 0) {
|
5125 |
-
$error = 1;
|
5126 |
-
|
5127 |
-
//debugpng
|
5128 |
-
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5129 |
-
print '[addPngFromFile unsupported compression method ' . $file . ']';
|
5130 |
-
}
|
5131 |
-
|
5132 |
-
$errormsg = 'unsupported compression method';
|
5133 |
-
}
|
5134 |
-
|
5135 |
-
if ($info['filterMethod'] != 0) {
|
5136 |
-
$error = 1;
|
5137 |
-
|
5138 |
-
//debugpng
|
5139 |
-
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5140 |
-
print '[addPngFromFile unsupported filter method ' . $file . ']';
|
5141 |
-
}
|
5142 |
-
|
5143 |
-
$errormsg = 'unsupported filter method';
|
5144 |
-
}
|
5145 |
-
break;
|
5146 |
-
|
5147 |
-
case 'PLTE':
|
5148 |
-
$pdata .= mb_substr($data, $p + 8, $chunkLen, '8bit');
|
5149 |
-
break;
|
5150 |
-
|
5151 |
-
case 'IDAT':
|
5152 |
-
$idata .= mb_substr($data, $p + 8, $chunkLen, '8bit');
|
5153 |
-
break;
|
5154 |
-
|
5155 |
-
case 'tRNS':
|
5156 |
-
//this chunk can only occur once and it must occur after the PLTE chunk and before IDAT chunk
|
5157 |
-
//print "tRNS found, color type = ".$info['colorType']."\n";
|
5158 |
-
$transparency = array();
|
5159 |
-
|
5160 |
-
switch ($info['colorType']) {
|
5161 |
-
// indexed color, rbg
|
5162 |
-
case 3:
|
5163 |
-
/* corresponding to entries in the plte chunk
|
5164 |
-
Alpha for palette index 0: 1 byte
|
5165 |
-
Alpha for palette index 1: 1 byte
|
5166 |
-
...etc...
|
5167 |
-
*/
|
5168 |
-
// there will be one entry for each palette entry. up until the last non-opaque entry.
|
5169 |
-
// set up an array, stretching over all palette entries which will be o (opaque) or 1 (transparent)
|
5170 |
-
$transparency['type'] = 'indexed';
|
5171 |
-
$trans = 0;
|
5172 |
-
|
5173 |
-
for ($i = $chunkLen; $i >= 0; $i--) {
|
5174 |
-
if (ord($data[$p + 8 + $i]) == 0) {
|
5175 |
-
$trans = $i;
|
5176 |
-
}
|
5177 |
-
}
|
5178 |
-
|
5179 |
-
$transparency['data'] = $trans;
|
5180 |
-
break;
|
5181 |
-
|
5182 |
-
// grayscale
|
5183 |
-
case 0:
|
5184 |
-
/* corresponding to entries in the plte chunk
|
5185 |
-
Gray: 2 bytes, range 0 .. (2^bitdepth)-1
|
5186 |
-
*/
|
5187 |
-
// $transparency['grayscale'] = $this->PRVT_getBytes($data,$p+8,2); // g = grayscale
|
5188 |
-
$transparency['type'] = 'indexed';
|
5189 |
-
$transparency['data'] = ord($data[$p + 8 + 1]);
|
5190 |
-
break;
|
5191 |
-
|
5192 |
-
// truecolor
|
5193 |
-
case 2:
|
5194 |
-
/* corresponding to entries in the plte chunk
|
5195 |
-
Red: 2 bytes, range 0 .. (2^bitdepth)-1
|
5196 |
-
Green: 2 bytes, range 0 .. (2^bitdepth)-1
|
5197 |
-
Blue: 2 bytes, range 0 .. (2^bitdepth)-1
|
5198 |
-
*/
|
5199 |
-
$transparency['r'] = $this->getBytes($data, $p + 8, 2);
|
5200 |
-
// r from truecolor
|
5201 |
-
$transparency['g'] = $this->getBytes($data, $p + 10, 2);
|
5202 |
-
// g from truecolor
|
5203 |
-
$transparency['b'] = $this->getBytes($data, $p + 12, 2);
|
5204 |
-
// b from truecolor
|
5205 |
-
|
5206 |
-
$transparency['type'] = 'color-key';
|
5207 |
-
break;
|
5208 |
-
|
5209 |
-
//unsupported transparency type
|
5210 |
-
default:
|
5211 |
-
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5212 |
-
print '[addPngFromFile unsupported transparency type ' . $file . ']';
|
5213 |
-
}
|
5214 |
-
break;
|
5215 |
-
}
|
5216 |
-
|
5217 |
-
// KS End new code
|
5218 |
-
break;
|
5219 |
-
|
5220 |
-
default:
|
5221 |
-
break;
|
5222 |
-
}
|
5223 |
-
|
5224 |
-
$p += $chunkLen + 12;
|
5225 |
-
}
|
5226 |
-
|
5227 |
-
if (!$haveHeader) {
|
5228 |
-
$error = 1;
|
5229 |
-
|
5230 |
-
//debugpng
|
5231 |
-
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5232 |
-
print '[addPngFromFile information header is missing ' . $file . ']';
|
5233 |
-
}
|
5234 |
-
|
5235 |
-
$errormsg = 'information header is missing';
|
5236 |
-
}
|
5237 |
-
|
5238 |
-
if (isset($info['interlaceMethod']) && $info['interlaceMethod']) {
|
5239 |
-
$error = 1;
|
5240 |
-
|
5241 |
-
//debugpng
|
5242 |
-
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5243 |
-
print '[addPngFromFile no support for interlaced images in pdf ' . $file . ']';
|
5244 |
-
}
|
5245 |
-
|
5246 |
-
$errormsg = 'There appears to be no support for interlaced images in pdf.';
|
5247 |
-
}
|
5248 |
-
}
|
5249 |
-
|
5250 |
-
if (!$error && $info['bitDepth'] > 8) {
|
5251 |
-
$error = 1;
|
5252 |
-
|
5253 |
-
//debugpng
|
5254 |
-
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5255 |
-
print '[addPngFromFile bit depth of 8 or less is supported ' . $file . ']';
|
5256 |
-
}
|
5257 |
-
|
5258 |
-
$errormsg = 'only bit depth of 8 or less is supported';
|
5259 |
-
}
|
5260 |
-
|
5261 |
-
if (!$error) {
|
5262 |
-
switch ($info['colorType']) {
|
5263 |
-
case 3:
|
5264 |
-
$color = 'DeviceRGB';
|
5265 |
-
$ncolor = 1;
|
5266 |
-
break;
|
5267 |
-
|
5268 |
-
case 2:
|
5269 |
-
$color = 'DeviceRGB';
|
5270 |
-
$ncolor = 3;
|
5271 |
-
break;
|
5272 |
-
|
5273 |
-
case 0:
|
5274 |
-
$color = 'DeviceGray';
|
5275 |
-
$ncolor = 1;
|
5276 |
-
break;
|
5277 |
-
|
5278 |
-
default:
|
5279 |
-
$error = 1;
|
5280 |
-
|
5281 |
-
//debugpng
|
5282 |
-
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5283 |
-
print '[addPngFromFile alpha channel not supported: ' . $info['colorType'] . ' ' . $file . ']';
|
5284 |
-
}
|
5285 |
-
|
5286 |
-
$errormsg = 'transparency alpha channel not supported, transparency only supported for palette images.';
|
5287 |
-
}
|
5288 |
-
}
|
5289 |
-
|
5290 |
-
if ($error) {
|
5291 |
-
$this->addMessage('PNG error - (' . $file . ') ' . $errormsg);
|
5292 |
-
|
5293 |
-
return;
|
5294 |
-
}
|
5295 |
-
|
5296 |
-
//print_r($info);
|
5297 |
-
// so this image is ok... add it in.
|
5298 |
-
$this->numImages++;
|
5299 |
-
$im = $this->numImages;
|
5300 |
-
$label = "I$im";
|
5301 |
-
$this->numObj++;
|
5302 |
-
|
5303 |
-
// $this->o_image($this->numObj,'new',array('label' => $label,'data' => $idata,'iw' => $w,'ih' => $h,'type' => 'png','ic' => $info['width']));
|
5304 |
-
$options = array(
|
5305 |
-
'label' => $label,
|
5306 |
-
'data' => $idata,
|
5307 |
-
'bitsPerComponent' => $info['bitDepth'],
|
5308 |
-
'pdata' => $pdata,
|
5309 |
-
'iw' => $info['width'],
|
5310 |
-
'ih' => $info['height'],
|
5311 |
-
'type' => 'png',
|
5312 |
-
'color' => $color,
|
5313 |
-
'ncolor' => $ncolor,
|
5314 |
-
'masked' => $mask,
|
5315 |
-
'isMask' => $is_mask
|
5316 |
-
);
|
5317 |
-
|
5318 |
-
if (isset($transparency)) {
|
5319 |
-
$options['transparency'] = $transparency;
|
5320 |
-
}
|
5321 |
-
|
5322 |
-
$this->o_image($this->numObj, 'new', $options);
|
5323 |
-
$this->imagelist[$file] = array('label' => $label, 'w' => $info['width'], 'h' => $info['height']);
|
5324 |
-
}
|
5325 |
-
|
5326 |
-
if ($is_mask) {
|
5327 |
-
return;
|
5328 |
-
}
|
5329 |
-
|
5330 |
-
if ($w <= 0 && $h <= 0) {
|
5331 |
-
$w = $info['width'];
|
5332 |
-
$h = $info['height'];
|
5333 |
-
}
|
5334 |
-
|
5335 |
-
if ($w <= 0) {
|
5336 |
-
$w = $h / $info['height'] * $info['width'];
|
5337 |
-
}
|
5338 |
-
|
5339 |
-
if ($h <= 0) {
|
5340 |
-
$h = $w * $info['height'] / $info['width'];
|
5341 |
-
}
|
5342 |
-
|
5343 |
-
$this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ", $w, $h, $x, $y, $label));
|
5344 |
-
}
|
5345 |
-
|
5346 |
-
/**
|
5347 |
-
* add a JPEG image into the document, from a file
|
5348 |
-
*
|
5349 |
-
* @param $img
|
5350 |
-
* @param $x
|
5351 |
-
* @param $y
|
5352 |
-
* @param int $w
|
5353 |
-
* @param int $h
|
5354 |
-
*/
|
5355 |
-
function addJpegFromFile($img, $x, $y, $w = 0, $h = 0)
|
5356 |
-
{
|
5357 |
-
// attempt to add a jpeg image straight from a file, using no GD commands
|
5358 |
-
// note that this function is unable to operate on a remote file.
|
5359 |
-
|
5360 |
-
if (!file_exists($img)) {
|
5361 |
-
return;
|
5362 |
-
}
|
5363 |
-
|
5364 |
-
if ($this->image_iscached($img)) {
|
5365 |
-
$data = null;
|
5366 |
-
$imageWidth = $this->imagelist[$img]['w'];
|
5367 |
-
$imageHeight = $this->imagelist[$img]['h'];
|
5368 |
-
$channels = $this->imagelist[$img]['c'];
|
5369 |
-
} else {
|
5370 |
-
$tmp = getimagesize($img);
|
5371 |
-
$imageWidth = $tmp[0];
|
5372 |
-
$imageHeight = $tmp[1];
|
5373 |
-
|
5374 |
-
if (isset($tmp['channels'])) {
|
5375 |
-
$channels = $tmp['channels'];
|
5376 |
-
} else {
|
5377 |
-
$channels = 3;
|
5378 |
-
}
|
5379 |
-
|
5380 |
-
$data = file_get_contents($img);
|
5381 |
-
}
|
5382 |
-
|
5383 |
-
if ($w <= 0 && $h <= 0) {
|
5384 |
-
$w = $imageWidth;
|
5385 |
-
}
|
5386 |
-
|
5387 |
-
if ($w == 0) {
|
5388 |
-
$w = $h / $imageHeight * $imageWidth;
|
5389 |
-
}
|
5390 |
-
|
5391 |
-
if ($h == 0) {
|
5392 |
-
$h = $w * $imageHeight / $imageWidth;
|
5393 |
-
}
|
5394 |
-
|
5395 |
-
$this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight, $channels, $img);
|
5396 |
-
}
|
5397 |
-
|
5398 |
-
/**
|
5399 |
-
* common code used by the two JPEG adding functions
|
5400 |
-
* @param $data
|
5401 |
-
* @param $x
|
5402 |
-
* @param $y
|
5403 |
-
* @param int $w
|
5404 |
-
* @param int $h
|
5405 |
-
* @param $imageWidth
|
5406 |
-
* @param $imageHeight
|
5407 |
-
* @param int $channels
|
5408 |
-
* @param $imgname
|
5409 |
-
*/
|
5410 |
-
private function addJpegImage_common(
|
5411 |
-
&$data,
|
5412 |
-
$x,
|
5413 |
-
$y,
|
5414 |
-
$w = 0,
|
5415 |
-
$h = 0,
|
5416 |
-
$imageWidth,
|
5417 |
-
$imageHeight,
|
5418 |
-
$channels = 3,
|
5419 |
-
$imgname
|
5420 |
-
) {
|
5421 |
-
if ($this->image_iscached($imgname)) {
|
5422 |
-
$label = $this->imagelist[$imgname]['label'];
|
5423 |
-
//debugpng
|
5424 |
-
//if (DEBUGPNG) print '[addJpegImage_common Duplicate '.$imgname.']';
|
5425 |
-
|
5426 |
-
} else {
|
5427 |
-
if ($data == null) {
|
5428 |
-
$this->addMessage('addJpegImage_common error - (' . $imgname . ') data not present!');
|
5429 |
-
|
5430 |
-
return;
|
5431 |
-
}
|
5432 |
-
|
5433 |
-
// note that this function is not to be called externally
|
5434 |
-
// it is just the common code between the GD and the file options
|
5435 |
-
$this->numImages++;
|
5436 |
-
$im = $this->numImages;
|
5437 |
-
$label = "I$im";
|
5438 |
-
$this->numObj++;
|
5439 |
-
|
5440 |
-
$this->o_image(
|
5441 |
-
$this->numObj,
|
5442 |
-
'new',
|
5443 |
-
array(
|
5444 |
-
'label' => $label,
|
5445 |
-
'data' => &$data,
|
5446 |
-
'iw' => $imageWidth,
|
5447 |
-
'ih' => $imageHeight,
|
5448 |
-
'channels' => $channels
|
5449 |
-
)
|
5450 |
-
);
|
5451 |
-
|
5452 |
-
$this->imagelist[$imgname] = array(
|
5453 |
-
'label' => $label,
|
5454 |
-
'w' => $imageWidth,
|
5455 |
-
'h' => $imageHeight,
|
5456 |
-
'c' => $channels
|
5457 |
-
);
|
5458 |
-
}
|
5459 |
-
|
5460 |
-
$this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ ", $w, $h, $x, $y, $label));
|
5461 |
-
}
|
5462 |
-
|
5463 |
-
/**
|
5464 |
-
* specify where the document should open when it first starts
|
5465 |
-
*
|
5466 |
-
* @param $style
|
5467 |
-
* @param int $a
|
5468 |
-
* @param int $b
|
5469 |
-
* @param int $c
|
5470 |
-
*/
|
5471 |
-
function openHere($style, $a = 0, $b = 0, $c = 0)
|
5472 |
-
{
|
5473 |
-
// this function will open the document at a specified page, in a specified style
|
5474 |
-
// the values for style, and the required parameters are:
|
5475 |
-
// 'XYZ' left, top, zoom
|
5476 |
-
// 'Fit'
|
5477 |
-
// 'FitH' top
|
5478 |
-
// 'FitV' left
|
5479 |
-
// 'FitR' left,bottom,right
|
5480 |
-
// 'FitB'
|
5481 |
-
// 'FitBH' top
|
5482 |
-
// 'FitBV' left
|
5483 |
-
$this->numObj++;
|
5484 |
-
$this->o_destination(
|
5485 |
-
$this->numObj,
|
5486 |
-
'new',
|
5487 |
-
array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c)
|
5488 |
-
);
|
5489 |
-
$id = $this->catalogId;
|
5490 |
-
$this->o_catalog($id, 'openHere', $this->numObj);
|
5491 |
-
}
|
5492 |
-
|
5493 |
-
/**
|
5494 |
-
* Add JavaScript code to the PDF document
|
5495 |
-
*
|
5496 |
-
* @param string $code
|
5497 |
-
*/
|
5498 |
-
function addJavascript($code)
|
5499 |
-
{
|
5500 |
-
$this->javascript .= $code;
|
5501 |
-
}
|
5502 |
-
|
5503 |
-
/**
|
5504 |
-
* create a labelled destination within the document
|
5505 |
-
*
|
5506 |
-
* @param $label
|
5507 |
-
* @param $style
|
5508 |
-
* @param int $a
|
5509 |
-
* @param int $b
|
5510 |
-
* @param int $c
|
5511 |
-
*/
|
5512 |
-
function addDestination($label, $style, $a = 0, $b = 0, $c = 0)
|
5513 |
-
{
|
5514 |
-
// associates the given label with the destination, it is done this way so that a destination can be specified after
|
5515 |
-
// it has been linked to
|
5516 |
-
// styles are the same as the 'openHere' function
|
5517 |
-
$this->numObj++;
|
5518 |
-
$this->o_destination(
|
5519 |
-
$this->numObj,
|
5520 |
-
'new',
|
5521 |
-
array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c)
|
5522 |
-
);
|
5523 |
-
$id = $this->numObj;
|
5524 |
-
|
5525 |
-
// store the label->idf relationship, note that this means that labels can be used only once
|
5526 |
-
$this->destinations["$label"] = $id;
|
5527 |
-
}
|
5528 |
-
|
5529 |
-
/**
|
5530 |
-
* define font families, this is used to initialize the font families for the default fonts
|
5531 |
-
* and for the user to add new ones for their fonts. The default bahavious can be overridden should
|
5532 |
-
* that be desired.
|
5533 |
-
*
|
5534 |
-
* @param $family
|
5535 |
-
* @param string $options
|
5536 |
-
*/
|
5537 |
-
function setFontFamily($family, $options = '')
|
5538 |
-
{
|
5539 |
-
if (!is_array($options)) {
|
5540 |
-
if ($family === 'init') {
|
5541 |
-
// set the known family groups
|
5542 |
-
// these font families will be used to enable bold and italic markers to be included
|
5543 |
-
// within text streams. html forms will be used... <b></b> <i></i>
|
5544 |
-
$this->fontFamilies['Helvetica.afm'] =
|
5545 |
-
array(
|
5546 |
-
'b' => 'Helvetica-Bold.afm',
|
5547 |
-
'i' => 'Helvetica-Oblique.afm',
|
5548 |
-
'bi' => 'Helvetica-BoldOblique.afm',
|
5549 |
-
'ib' => 'Helvetica-BoldOblique.afm'
|
5550 |
-
);
|
5551 |
-
|
5552 |
-
$this->fontFamilies['Courier.afm'] =
|
5553 |
-
array(
|
5554 |
-
'b' => 'Courier-Bold.afm',
|
5555 |
-
'i' => 'Courier-Oblique.afm',
|
5556 |
-
'bi' => 'Courier-BoldOblique.afm',
|
5557 |
-
'ib' => 'Courier-BoldOblique.afm'
|
5558 |
-
);
|
5559 |
-
|
5560 |
-
$this->fontFamilies['Times-Roman.afm'] =
|
5561 |
-
array(
|
5562 |
-
'b' => 'Times-Bold.afm',
|
5563 |
-
'i' => 'Times-Italic.afm',
|
5564 |
-
'bi' => 'Times-BoldItalic.afm',
|
5565 |
-
'ib' => 'Times-BoldItalic.afm'
|
5566 |
-
);
|
5567 |
-
}
|
5568 |
-
} else {
|
5569 |
-
|
5570 |
-
// the user is trying to set a font family
|
5571 |
-
// note that this can also be used to set the base ones to something else
|
5572 |
-
if (mb_strlen($family)) {
|
5573 |
-
$this->fontFamilies[$family] = $options;
|
5574 |
-
}
|
5575 |
-
}
|
5576 |
-
}
|
5577 |
-
|
5578 |
-
/**
|
5579 |
-
* used to add messages for use in debugging
|
5580 |
-
*
|
5581 |
-
* @param $message
|
5582 |
-
*/
|
5583 |
-
function addMessage($message)
|
5584 |
-
{
|
5585 |
-
$this->messages .= $message . "\n";
|
5586 |
-
}
|
5587 |
-
|
5588 |
-
/**
|
5589 |
-
* a few functions which should allow the document to be treated transactionally.
|
5590 |
-
*
|
5591 |
-
* @param $action
|
5592 |
-
*/
|
5593 |
-
function transaction($action)
|
5594 |
-
{
|
5595 |
-
switch ($action) {
|
5596 |
-
case 'start':
|
5597 |
-
// store all the data away into the checkpoint variable
|
5598 |
-
$data = get_object_vars($this);
|
5599 |
-
$this->checkpoint = $data;
|
5600 |
-
unset($data);
|
5601 |
-
break;
|
5602 |
-
|
5603 |
-
case 'commit':
|
5604 |
-
if (is_array($this->checkpoint) && isset($this->checkpoint['checkpoint'])) {
|
5605 |
-
$tmp = $this->checkpoint['checkpoint'];
|
5606 |
-
$this->checkpoint = $tmp;
|
5607 |
-
unset($tmp);
|
5608 |
-
} else {
|
5609 |
-
$this->checkpoint = '';
|
5610 |
-
}
|
5611 |
-
break;
|
5612 |
-
|
5613 |
-
case 'rewind':
|
5614 |
-
// do not destroy the current checkpoint, but move us back to the state then, so that we can try again
|
5615 |
-
if (is_array($this->checkpoint)) {
|
5616 |
-
// can only abort if were inside a checkpoint
|
5617 |
-
$tmp = $this->checkpoint;
|
5618 |
-
|
5619 |
-
foreach ($tmp as $k => $v) {
|
5620 |
-
if ($k !== 'checkpoint') {
|
5621 |
-
$this->$k = $v;
|
5622 |
-
}
|
5623 |
-
}
|
5624 |
-
unset($tmp);
|
5625 |
-
}
|
5626 |
-
break;
|
5627 |
-
|
5628 |
-
case 'abort':
|
5629 |
-
if (is_array($this->checkpoint)) {
|
5630 |
-
// can only abort if were inside a checkpoint
|
5631 |
-
$tmp = $this->checkpoint;
|
5632 |
-
foreach ($tmp as $k => $v) {
|
5633 |
-
$this->$k = $v;
|
5634 |
-
}
|
5635 |
-
unset($tmp);
|
5636 |
-
}
|
5637 |
-
break;
|
5638 |
-
}
|
5639 |
-
}
|
5640 |
-
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* A PHP class to provide the basic functionality to create a pdf document without
|
4 |
+
* any requirement for additional modules.
|
5 |
+
*
|
6 |
+
* Extended by Orion Richardson to support Unicode / UTF-8 characters using
|
7 |
+
* TCPDF and others as a guide.
|
8 |
+
*
|
9 |
+
* @author Wayne Munro <pdf@ros.co.nz>
|
10 |
+
* @author Orion Richardson <orionr@yahoo.com>
|
11 |
+
* @author Helmut Tischer <htischer@weihenstephan.org>
|
12 |
+
* @author Ryan H. Masten <ryan.masten@gmail.com>
|
13 |
+
* @author Brian Sweeney <eclecticgeek@gmail.com>
|
14 |
+
* @author Fabien Ménager <fabien.menager@gmail.com>
|
15 |
+
* @license Public Domain http://creativecommons.org/licenses/publicdomain/
|
16 |
+
* @package Cpdf
|
17 |
+
*/
|
18 |
+
use FontLib\Font;
|
19 |
+
use FontLib\BinaryStream;
|
20 |
+
|
21 |
+
class Cpdf
|
22 |
+
{
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @var integer The current number of pdf objects in the document
|
26 |
+
*/
|
27 |
+
public $numObj = 0;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @var array This array contains all of the pdf objects, ready for final assembly
|
31 |
+
*/
|
32 |
+
public $objects = array();
|
33 |
+
|
34 |
+
/**
|
35 |
+
* @var integer The objectId (number within the objects array) of the document catalog
|
36 |
+
*/
|
37 |
+
public $catalogId;
|
38 |
+
|
39 |
+
/**
|
40 |
+
* @var array Array carrying information about the fonts that the system currently knows about
|
41 |
+
* Used to ensure that a font is not loaded twice, among other things
|
42 |
+
*/
|
43 |
+
public $fonts = array();
|
44 |
+
|
45 |
+
/**
|
46 |
+
* @var string The default font metrics file to use if no other font has been loaded.
|
47 |
+
* The path to the directory containing the font metrics should be included
|
48 |
+
*/
|
49 |
+
public $defaultFont = './fonts/Helvetica.afm';
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @string A record of the current font
|
53 |
+
*/
|
54 |
+
public $currentFont = '';
|
55 |
+
|
56 |
+
/**
|
57 |
+
* @var string The current base font
|
58 |
+
*/
|
59 |
+
public $currentBaseFont = '';
|
60 |
+
|
61 |
+
/**
|
62 |
+
* @var integer The number of the current font within the font array
|
63 |
+
*/
|
64 |
+
public $currentFontNum = 0;
|
65 |
+
|
66 |
+
/**
|
67 |
+
* @var integer
|
68 |
+
*/
|
69 |
+
public $currentNode;
|
70 |
+
|
71 |
+
/**
|
72 |
+
* @var integer Object number of the current page
|
73 |
+
*/
|
74 |
+
public $currentPage;
|
75 |
+
|
76 |
+
/**
|
77 |
+
* @var integer Object number of the currently active contents block
|
78 |
+
*/
|
79 |
+
public $currentContents;
|
80 |
+
|
81 |
+
/**
|
82 |
+
* @var integer Number of fonts within the system
|
83 |
+
*/
|
84 |
+
public $numFonts = 0;
|
85 |
+
|
86 |
+
/**
|
87 |
+
* @var integer Number of graphic state resources used
|
88 |
+
*/
|
89 |
+
private $numStates = 0;
|
90 |
+
|
91 |
+
/**
|
92 |
+
* @var array Number of graphic state resources used
|
93 |
+
*/
|
94 |
+
private $gstates = array();
|
95 |
+
|
96 |
+
/**
|
97 |
+
* @var array Current color for fill operations, defaults to inactive value,
|
98 |
+
* all three components should be between 0 and 1 inclusive when active
|
99 |
+
*/
|
100 |
+
public $currentColor = null;
|
101 |
+
|
102 |
+
/**
|
103 |
+
* @var array Current color for stroke operations (lines etc.)
|
104 |
+
*/
|
105 |
+
public $currentStrokeColor = null;
|
106 |
+
|
107 |
+
/**
|
108 |
+
* @var string Fill rule (nonzero or evenodd)
|
109 |
+
*/
|
110 |
+
public $fillRule = "nonzero";
|
111 |
+
|
112 |
+
/**
|
113 |
+
* @var string Current style that lines are drawn in
|
114 |
+
*/
|
115 |
+
public $currentLineStyle = '';
|
116 |
+
|
117 |
+
/**
|
118 |
+
* @var array Current line transparency (partial graphics state)
|
119 |
+
*/
|
120 |
+
public $currentLineTransparency = array("mode" => "Normal", "opacity" => 1.0);
|
121 |
+
|
122 |
+
/**
|
123 |
+
* array Current fill transparency (partial graphics state)
|
124 |
+
*/
|
125 |
+
public $currentFillTransparency = array("mode" => "Normal", "opacity" => 1.0);
|
126 |
+
|
127 |
+
/**
|
128 |
+
* @var array An array which is used to save the state of the document, mainly the colors and styles
|
129 |
+
* it is used to temporarily change to another state, then change back to what it was before
|
130 |
+
*/
|
131 |
+
public $stateStack = array();
|
132 |
+
|
133 |
+
/**
|
134 |
+
* @var integer Number of elements within the state stack
|
135 |
+
*/
|
136 |
+
public $nStateStack = 0;
|
137 |
+
|
138 |
+
/**
|
139 |
+
* @var integer Number of page objects within the document
|
140 |
+
*/
|
141 |
+
public $numPages = 0;
|
142 |
+
|
143 |
+
/**
|
144 |
+
* @var array Object Id storage stack
|
145 |
+
*/
|
146 |
+
public $stack = array();
|
147 |
+
|
148 |
+
/**
|
149 |
+
* @var integer Number of elements within the object Id storage stack
|
150 |
+
*/
|
151 |
+
public $nStack = 0;
|
152 |
+
|
153 |
+
/**
|
154 |
+
* an array which contains information about the objects which are not firmly attached to pages
|
155 |
+
* these have been added with the addObject function
|
156 |
+
*/
|
157 |
+
public $looseObjects = array();
|
158 |
+
|
159 |
+
/**
|
160 |
+
* array contains information about how the loose objects are to be added to the document
|
161 |
+
*/
|
162 |
+
public $addLooseObjects = array();
|
163 |
+
|
164 |
+
/**
|
165 |
+
* @var integer The objectId of the information object for the document
|
166 |
+
* this contains authorship, title etc.
|
167 |
+
*/
|
168 |
+
public $infoObject = 0;
|
169 |
+
|
170 |
+
/**
|
171 |
+
* @var integer Number of images being tracked within the document
|
172 |
+
*/
|
173 |
+
public $numImages = 0;
|
174 |
+
|
175 |
+
/**
|
176 |
+
* @var array An array containing options about the document
|
177 |
+
* it defaults to turning on the compression of the objects
|
178 |
+
*/
|
179 |
+
public $options = array('compression' => true);
|
180 |
+
|
181 |
+
/**
|
182 |
+
* @var integer The objectId of the first page of the document
|
183 |
+
*/
|
184 |
+
public $firstPageId;
|
185 |
+
|
186 |
+
/**
|
187 |
+
* @var integer The object Id of the procset object
|
188 |
+
*/
|
189 |
+
public $procsetObjectId;
|
190 |
+
|
191 |
+
/**
|
192 |
+
* @var array Store the information about the relationship between font families
|
193 |
+
* this used so that the code knows which font is the bold version of another font, etc.
|
194 |
+
* the value of this array is initialised in the constructor function.
|
195 |
+
*/
|
196 |
+
public $fontFamilies = array();
|
197 |
+
|
198 |
+
/**
|
199 |
+
* @var string Folder for php serialized formats of font metrics files.
|
200 |
+
* If empty string, use same folder as original metrics files.
|
201 |
+
* This can be passed in from class creator.
|
202 |
+
* If this folder does not exist or is not writable, Cpdf will be **much** slower.
|
203 |
+
* Because of potential trouble with php safe mode, folder cannot be created at runtime.
|
204 |
+
*/
|
205 |
+
public $fontcache = '';
|
206 |
+
|
207 |
+
/**
|
208 |
+
* @var integer The version of the font metrics cache file.
|
209 |
+
* This value must be manually incremented whenever the internal font data structure is modified.
|
210 |
+
*/
|
211 |
+
public $fontcacheVersion = 6;
|
212 |
+
|
213 |
+
/**
|
214 |
+
* @var string Temporary folder.
|
215 |
+
* If empty string, will attempt system tmp folder.
|
216 |
+
* This can be passed in from class creator.
|
217 |
+
*/
|
218 |
+
public $tmp = '';
|
219 |
+
|
220 |
+
/**
|
221 |
+
* @var string Track if the current font is bolded or italicised
|
222 |
+
*/
|
223 |
+
public $currentTextState = '';
|
224 |
+
|
225 |
+
/**
|
226 |
+
* @var string Messages are stored here during processing, these can be selected afterwards to give some useful debug information
|
227 |
+
*/
|
228 |
+
public $messages = '';
|
229 |
+
|
230 |
+
/**
|
231 |
+
* @var string The encryption array for the document encryption is stored here
|
232 |
+
*/
|
233 |
+
public $arc4 = '';
|
234 |
+
|
235 |
+
/**
|
236 |
+
* @var integer The object Id of the encryption information
|
237 |
+
*/
|
238 |
+
public $arc4_objnum = 0;
|
239 |
+
|
240 |
+
/**
|
241 |
+
* @var string The file identifier, used to uniquely identify a pdf document
|
242 |
+
*/
|
243 |
+
public $fileIdentifier = '';
|
244 |
+
|
245 |
+
/**
|
246 |
+
* @var boolean A flag to say if a document is to be encrypted or not
|
247 |
+
*/
|
248 |
+
public $encrypted = false;
|
249 |
+
|
250 |
+
/**
|
251 |
+
* @var string The encryption key for the encryption of all the document content (structure is not encrypted)
|
252 |
+
*/
|
253 |
+
public $encryptionKey = '';
|
254 |
+
|
255 |
+
/**
|
256 |
+
* @var array Array which forms a stack to keep track of nested callback functions
|
257 |
+
*/
|
258 |
+
public $callback = array();
|
259 |
+
|
260 |
+
/**
|
261 |
+
* @var integer The number of callback functions in the callback array
|
262 |
+
*/
|
263 |
+
public $nCallback = 0;
|
264 |
+
|
265 |
+
/**
|
266 |
+
* @var array Store label->id pairs for named destinations, these will be used to replace internal links
|
267 |
+
* done this way so that destinations can be defined after the location that links to them
|
268 |
+
*/
|
269 |
+
public $destinations = array();
|
270 |
+
|
271 |
+
/**
|
272 |
+
* @var array Store the stack for the transaction commands, each item in here is a record of the values of all the
|
273 |
+
* publiciables within the class, so that the user can rollback at will (from each 'start' command)
|
274 |
+
* note that this includes the objects array, so these can be large.
|
275 |
+
*/
|
276 |
+
public $checkpoint = '';
|
277 |
+
|
278 |
+
/**
|
279 |
+
* @var array Table of Image origin filenames and image labels which were already added with o_image().
|
280 |
+
* Allows to merge identical images
|
281 |
+
*/
|
282 |
+
public $imagelist = array();
|
283 |
+
|
284 |
+
/**
|
285 |
+
* @var boolean Whether the text passed in should be treated as Unicode or just local character set.
|
286 |
+
*/
|
287 |
+
public $isUnicode = false;
|
288 |
+
|
289 |
+
/**
|
290 |
+
* @var string the JavaScript code of the document
|
291 |
+
*/
|
292 |
+
public $javascript = '';
|
293 |
+
|
294 |
+
/**
|
295 |
+
* @var boolean whether the compression is possible
|
296 |
+
*/
|
297 |
+
protected $compressionReady = false;
|
298 |
+
|
299 |
+
/**
|
300 |
+
* @var array Current page size
|
301 |
+
*/
|
302 |
+
protected $currentPageSize = array("width" => 0, "height" => 0);
|
303 |
+
|
304 |
+
/**
|
305 |
+
* @var array All the chars that will be required in the font subsets
|
306 |
+
*/
|
307 |
+
protected $stringSubsets = array();
|
308 |
+
|
309 |
+
/**
|
310 |
+
* @var string The target internal encoding
|
311 |
+
*/
|
312 |
+
static protected $targetEncoding = 'Windows-1252';
|
313 |
+
|
314 |
+
/**
|
315 |
+
* @var array The list of the core fonts
|
316 |
+
*/
|
317 |
+
static protected $coreFonts = array(
|
318 |
+
'courier',
|
319 |
+
'courier-bold',
|
320 |
+
'courier-oblique',
|
321 |
+
'courier-boldoblique',
|
322 |
+
'helvetica',
|
323 |
+
'helvetica-bold',
|
324 |
+
'helvetica-oblique',
|
325 |
+
'helvetica-boldoblique',
|
326 |
+
'times-roman',
|
327 |
+
'times-bold',
|
328 |
+
'times-italic',
|
329 |
+
'times-bolditalic',
|
330 |
+
'symbol',
|
331 |
+
'zapfdingbats'
|
332 |
+
);
|
333 |
+
|
334 |
+
/**
|
335 |
+
* Class constructor
|
336 |
+
* This will start a new document
|
337 |
+
*
|
338 |
+
* @param array $pageSize Array of 4 numbers, defining the bottom left and upper right corner of the page. first two are normally zero.
|
339 |
+
* @param boolean $isUnicode Whether text will be treated as Unicode or not.
|
340 |
+
* @param string $fontcache The font cache folder
|
341 |
+
* @param string $tmp The temporary folder
|
342 |
+
*/
|
343 |
+
function __construct($pageSize = array(0, 0, 612, 792), $isUnicode = false, $fontcache = '', $tmp = '')
|
344 |
+
{
|
345 |
+
$this->isUnicode = $isUnicode;
|
346 |
+
$this->fontcache = rtrim($fontcache, DIRECTORY_SEPARATOR."/\\");
|
347 |
+
$this->tmp = ($tmp !== '' ? $tmp : sys_get_temp_dir());
|
348 |
+
$this->newDocument($pageSize);
|
349 |
+
|
350 |
+
$this->compressionReady = function_exists('gzcompress');
|
351 |
+
|
352 |
+
if (in_array('Windows-1252', mb_list_encodings())) {
|
353 |
+
self::$targetEncoding = 'Windows-1252';
|
354 |
+
}
|
355 |
+
|
356 |
+
// also initialize the font families that are known about already
|
357 |
+
$this->setFontFamily('init');
|
358 |
+
}
|
359 |
+
|
360 |
+
/**
|
361 |
+
* Document object methods (internal use only)
|
362 |
+
*
|
363 |
+
* There is about one object method for each type of object in the pdf document
|
364 |
+
* Each function has the same call list ($id,$action,$options).
|
365 |
+
* $id = the object ID of the object, or what it is to be if it is being created
|
366 |
+
* $action = a string specifying the action to be performed, though ALL must support:
|
367 |
+
* 'new' - create the object with the id $id
|
368 |
+
* 'out' - produce the output for the pdf object
|
369 |
+
* $options = optional, a string or array containing the various parameters for the object
|
370 |
+
*
|
371 |
+
* These, in conjunction with the output function are the ONLY way for output to be produced
|
372 |
+
* within the pdf 'file'.
|
373 |
+
*/
|
374 |
+
|
375 |
+
/**
|
376 |
+
* Destination object, used to specify the location for the user to jump to, presently on opening
|
377 |
+
*
|
378 |
+
* @param $id
|
379 |
+
* @param $action
|
380 |
+
* @param string $options
|
381 |
+
* @return string|null
|
382 |
+
*/
|
383 |
+
protected function o_destination($id, $action, $options = '')
|
384 |
+
{
|
385 |
+
switch ($action) {
|
386 |
+
case 'new':
|
387 |
+
$this->objects[$id] = array('t' => 'destination', 'info' => array());
|
388 |
+
$tmp = '';
|
389 |
+
switch ($options['type']) {
|
390 |
+
case 'XYZ':
|
391 |
+
/** @noinspection PhpMissingBreakStatementInspection */
|
392 |
+
case 'FitR':
|
393 |
+
$tmp = ' ' . $options['p3'] . $tmp;
|
394 |
+
case 'FitH':
|
395 |
+
case 'FitV':
|
396 |
+
case 'FitBH':
|
397 |
+
/** @noinspection PhpMissingBreakStatementInspection */
|
398 |
+
case 'FitBV':
|
399 |
+
$tmp = ' ' . $options['p1'] . ' ' . $options['p2'] . $tmp;
|
400 |
+
case 'Fit':
|
401 |
+
case 'FitB':
|
402 |
+
$tmp = $options['type'] . $tmp;
|
403 |
+
$this->objects[$id]['info']['string'] = $tmp;
|
404 |
+
$this->objects[$id]['info']['page'] = $options['page'];
|
405 |
+
}
|
406 |
+
break;
|
407 |
+
|
408 |
+
case 'out':
|
409 |
+
$o = &$this->objects[$id];
|
410 |
+
|
411 |
+
$tmp = $o['info'];
|
412 |
+
$res = "\n$id 0 obj\n" . '[' . $tmp['page'] . ' 0 R /' . $tmp['string'] . "]\nendobj";
|
413 |
+
|
414 |
+
return $res;
|
415 |
+
}
|
416 |
+
|
417 |
+
return null;
|
418 |
+
}
|
419 |
+
|
420 |
+
/**
|
421 |
+
* set the viewer preferences
|
422 |
+
*
|
423 |
+
* @param $id
|
424 |
+
* @param $action
|
425 |
+
* @param string|array $options
|
426 |
+
* @return string|null
|
427 |
+
*/
|
428 |
+
protected function o_viewerPreferences($id, $action, $options = '')
|
429 |
+
{
|
430 |
+
switch ($action) {
|
431 |
+
case 'new':
|
432 |
+
$this->objects[$id] = array('t' => 'viewerPreferences', 'info' => array());
|
433 |
+
break;
|
434 |
+
|
435 |
+
case 'add':
|
436 |
+
$o = &$this->objects[$id];
|
437 |
+
|
438 |
+
foreach ($options as $k => $v) {
|
439 |
+
switch ($k) {
|
440 |
+
// Boolean keys
|
441 |
+
case 'HideToolbar':
|
442 |
+
case 'HideMenubar':
|
443 |
+
case 'HideWindowUI':
|
444 |
+
case 'FitWindow':
|
445 |
+
case 'CenterWindow':
|
446 |
+
case 'DisplayDocTitle':
|
447 |
+
case 'PickTrayByPDFSize':
|
448 |
+
$o['info'][$k] = (bool)$v;
|
449 |
+
break;
|
450 |
+
|
451 |
+
// Integer keys
|
452 |
+
case 'NumCopies':
|
453 |
+
$o['info'][$k] = (int)$v;
|
454 |
+
break;
|
455 |
+
|
456 |
+
// Name keys
|
457 |
+
case 'ViewArea':
|
458 |
+
case 'ViewClip':
|
459 |
+
case 'PrintClip':
|
460 |
+
case 'PrintArea':
|
461 |
+
$o['info'][$k] = (string)$v;
|
462 |
+
break;
|
463 |
+
|
464 |
+
// Named with limited valid values
|
465 |
+
case 'NonFullScreenPageMode':
|
466 |
+
if (!in_array($v, array('UseNone', 'UseOutlines', 'UseThumbs', 'UseOC'))) {
|
467 |
+
continue;
|
468 |
+
}
|
469 |
+
$o['info'][$k] = $v;
|
470 |
+
break;
|
471 |
+
|
472 |
+
case 'Direction':
|
473 |
+
if (!in_array($v, array('L2R', 'R2L'))) {
|
474 |
+
continue;
|
475 |
+
}
|
476 |
+
$o['info'][$k] = $v;
|
477 |
+
break;
|
478 |
+
|
479 |
+
case 'PrintScaling':
|
480 |
+
if (!in_array($v, array('None', 'AppDefault'))) {
|
481 |
+
continue;
|
482 |
+
}
|
483 |
+
$o['info'][$k] = $v;
|
484 |
+
break;
|
485 |
+
|
486 |
+
case 'Duplex':
|
487 |
+
if (!in_array($v, array('None', 'AppDefault'))) {
|
488 |
+
continue;
|
489 |
+
}
|
490 |
+
$o['info'][$k] = $v;
|
491 |
+
break;
|
492 |
+
|
493 |
+
// Integer array
|
494 |
+
case 'PrintPageRange':
|
495 |
+
// Cast to integer array
|
496 |
+
foreach ($v as $vK => $vV) {
|
497 |
+
$v[$vK] = (int)$vV;
|
498 |
+
}
|
499 |
+
$o['info'][$k] = array_values($v);
|
500 |
+
break;
|
501 |
+
}
|
502 |
+
}
|
503 |
+
break;
|
504 |
+
|
505 |
+
case 'out':
|
506 |
+
$o = &$this->objects[$id];
|
507 |
+
$res = "\n$id 0 obj\n<< ";
|
508 |
+
|
509 |
+
foreach ($o['info'] as $k => $v) {
|
510 |
+
if (is_string($v)) {
|
511 |
+
$v = '/' . $v;
|
512 |
+
} elseif (is_int($v)) {
|
513 |
+
$v = (string) $v;
|
514 |
+
} elseif (is_bool($v)) {
|
515 |
+
$v = ($v ? 'true' : 'false');
|
516 |
+
} elseif (is_array($v)) {
|
517 |
+
$v = '[' . implode(' ', $v) . ']';
|
518 |
+
}
|
519 |
+
$res .= "\n/$k $v";
|
520 |
+
}
|
521 |
+
$res .= "\n>>\n";
|
522 |
+
|
523 |
+
return $res;
|
524 |
+
}
|
525 |
+
|
526 |
+
return null;
|
527 |
+
}
|
528 |
+
|
529 |
+
/**
|
530 |
+
* define the document catalog, the overall controller for the document
|
531 |
+
*
|
532 |
+
* @param $id
|
533 |
+
* @param $action
|
534 |
+
* @param string|array $options
|
535 |
+
* @return string|null
|
536 |
+
*/
|
537 |
+
protected function o_catalog($id, $action, $options = '')
|
538 |
+
{
|
539 |
+
if ($action !== 'new') {
|
540 |
+
$o = &$this->objects[$id];
|
541 |
+
}
|
542 |
+
|
543 |
+
switch ($action) {
|
544 |
+
case 'new':
|
545 |
+
$this->objects[$id] = array('t' => 'catalog', 'info' => array());
|
546 |
+
$this->catalogId = $id;
|
547 |
+
break;
|
548 |
+
|
549 |
+
case 'outlines':
|
550 |
+
case 'pages':
|
551 |
+
case 'openHere':
|
552 |
+
case 'javascript':
|
553 |
+
$o['info'][$action] = $options;
|
554 |
+
break;
|
555 |
+
|
556 |
+
case 'viewerPreferences':
|
557 |
+
if (!isset($o['info']['viewerPreferences'])) {
|
558 |
+
$this->numObj++;
|
559 |
+
$this->o_viewerPreferences($this->numObj, 'new');
|
560 |
+
$o['info']['viewerPreferences'] = $this->numObj;
|
561 |
+
}
|
562 |
+
|
563 |
+
$vp = $o['info']['viewerPreferences'];
|
564 |
+
$this->o_viewerPreferences($vp, 'add', $options);
|
565 |
+
|
566 |
+
break;
|
567 |
+
|
568 |
+
case 'out':
|
569 |
+
$res = "\n$id 0 obj\n<< /Type /Catalog";
|
570 |
+
|
571 |
+
foreach ($o['info'] as $k => $v) {
|
572 |
+
switch ($k) {
|
573 |
+
case 'outlines':
|
574 |
+
$res .= "\n/Outlines $v 0 R";
|
575 |
+
break;
|
576 |
+
|
577 |
+
case 'pages':
|
578 |
+
$res .= "\n/Pages $v 0 R";
|
579 |
+
break;
|
580 |
+
|
581 |
+
case 'viewerPreferences':
|
582 |
+
$res .= "\n/ViewerPreferences $v 0 R";
|
583 |
+
break;
|
584 |
+
|
585 |
+
case 'openHere':
|
586 |
+
$res .= "\n/OpenAction $v 0 R";
|
587 |
+
break;
|
588 |
+
|
589 |
+
case 'javascript':
|
590 |
+
$res .= "\n/Names <</JavaScript $v 0 R>>";
|
591 |
+
break;
|
592 |
+
}
|
593 |
+
}
|
594 |
+
|
595 |
+
$res .= " >>\nendobj";
|
596 |
+
|
597 |
+
return $res;
|
598 |
+
}
|
599 |
+
|
600 |
+
return null;
|
601 |
+
}
|
602 |
+
|
603 |
+
/**
|
604 |
+
* object which is a parent to the pages in the document
|
605 |
+
*
|
606 |
+
* @param $id
|
607 |
+
* @param $action
|
608 |
+
* @param string $options
|
609 |
+
* @return string|null
|
610 |
+
*/
|
611 |
+
protected function o_pages($id, $action, $options = '')
|
612 |
+
{
|
613 |
+
if ($action !== 'new') {
|
614 |
+
$o = &$this->objects[$id];
|
615 |
+
}
|
616 |
+
|
617 |
+
switch ($action) {
|
618 |
+
case 'new':
|
619 |
+
$this->objects[$id] = array('t' => 'pages', 'info' => array());
|
620 |
+
$this->o_catalog($this->catalogId, 'pages', $id);
|
621 |
+
break;
|
622 |
+
|
623 |
+
case 'page':
|
624 |
+
if (!is_array($options)) {
|
625 |
+
// then it will just be the id of the new page
|
626 |
+
$o['info']['pages'][] = $options;
|
627 |
+
} else {
|
628 |
+
// then it should be an array having 'id','rid','pos', where rid=the page to which this one will be placed relative
|
629 |
+
// and pos is either 'before' or 'after', saying where this page will fit.
|
630 |
+
if (isset($options['id']) && isset($options['rid']) && isset($options['pos'])) {
|
631 |
+
$i = array_search($options['rid'], $o['info']['pages']);
|
632 |
+
if (isset($o['info']['pages'][$i]) && $o['info']['pages'][$i] == $options['rid']) {
|
633 |
+
|
634 |
+
// then there is a match
|
635 |
+
// make a space
|
636 |
+
switch ($options['pos']) {
|
637 |
+
case 'before':
|
638 |
+
$k = $i;
|
639 |
+
break;
|
640 |
+
|
641 |
+
case 'after':
|
642 |
+
$k = $i + 1;
|
643 |
+
break;
|
644 |
+
|
645 |
+
default:
|
646 |
+
$k = -1;
|
647 |
+
break;
|
648 |
+
}
|
649 |
+
|
650 |
+
if ($k >= 0) {
|
651 |
+
for ($j = count($o['info']['pages']) - 1; $j >= $k; $j--) {
|
652 |
+
$o['info']['pages'][$j + 1] = $o['info']['pages'][$j];
|
653 |
+
}
|
654 |
+
|
655 |
+
$o['info']['pages'][$k] = $options['id'];
|
656 |
+
}
|
657 |
+
}
|
658 |
+
}
|
659 |
+
}
|
660 |
+
break;
|
661 |
+
|
662 |
+
case 'procset':
|
663 |
+
$o['info']['procset'] = $options;
|
664 |
+
break;
|
665 |
+
|
666 |
+
case 'mediaBox':
|
667 |
+
$o['info']['mediaBox'] = $options;
|
668 |
+
// which should be an array of 4 numbers
|
669 |
+
$this->currentPageSize = array('width' => $options[2], 'height' => $options[3]);
|
670 |
+
break;
|
671 |
+
|
672 |
+
case 'font':
|
673 |
+
$o['info']['fonts'][] = array('objNum' => $options['objNum'], 'fontNum' => $options['fontNum']);
|
674 |
+
break;
|
675 |
+
|
676 |
+
case 'extGState':
|
677 |
+
$o['info']['extGStates'][] = array('objNum' => $options['objNum'], 'stateNum' => $options['stateNum']);
|
678 |
+
break;
|
679 |
+
|
680 |
+
case 'xObject':
|
681 |
+
$o['info']['xObjects'][] = array('objNum' => $options['objNum'], 'label' => $options['label']);
|
682 |
+
break;
|
683 |
+
|
684 |
+
case 'out':
|
685 |
+
if (count($o['info']['pages'])) {
|
686 |
+
$res = "\n$id 0 obj\n<< /Type /Pages\n/Kids [";
|
687 |
+
foreach ($o['info']['pages'] as $v) {
|
688 |
+
$res .= "$v 0 R\n";
|
689 |
+
}
|
690 |
+
|
691 |
+
$res .= "]\n/Count " . count($this->objects[$id]['info']['pages']);
|
692 |
+
|
693 |
+
if ((isset($o['info']['fonts']) && count($o['info']['fonts'])) ||
|
694 |
+
isset($o['info']['procset']) ||
|
695 |
+
(isset($o['info']['extGStates']) && count($o['info']['extGStates']))
|
696 |
+
) {
|
697 |
+
$res .= "\n/Resources <<";
|
698 |
+
|
699 |
+
if (isset($o['info']['procset'])) {
|
700 |
+
$res .= "\n/ProcSet " . $o['info']['procset'] . " 0 R";
|
701 |
+
}
|
702 |
+
|
703 |
+
if (isset($o['info']['fonts']) && count($o['info']['fonts'])) {
|
704 |
+
$res .= "\n/Font << ";
|
705 |
+
foreach ($o['info']['fonts'] as $finfo) {
|
706 |
+
$res .= "\n/F" . $finfo['fontNum'] . " " . $finfo['objNum'] . " 0 R";
|
707 |
+
}
|
708 |
+
$res .= "\n>>";
|
709 |
+
}
|
710 |
+
|
711 |
+
if (isset($o['info']['xObjects']) && count($o['info']['xObjects'])) {
|
712 |
+
$res .= "\n/XObject << ";
|
713 |
+
foreach ($o['info']['xObjects'] as $finfo) {
|
714 |
+
$res .= "\n/" . $finfo['label'] . " " . $finfo['objNum'] . " 0 R";
|
715 |
+
}
|
716 |
+
$res .= "\n>>";
|
717 |
+
}
|
718 |
+
|
719 |
+
if (isset($o['info']['extGStates']) && count($o['info']['extGStates'])) {
|
720 |
+
$res .= "\n/ExtGState << ";
|
721 |
+
foreach ($o['info']['extGStates'] as $gstate) {
|
722 |
+
$res .= "\n/GS" . $gstate['stateNum'] . " " . $gstate['objNum'] . " 0 R";
|
723 |
+
}
|
724 |
+
$res .= "\n>>";
|
725 |
+
}
|
726 |
+
|
727 |
+
$res .= "\n>>";
|
728 |
+
if (isset($o['info']['mediaBox'])) {
|
729 |
+
$tmp = $o['info']['mediaBox'];
|
730 |
+
$res .= "\n/MediaBox [" . sprintf(
|
731 |
+
'%.3F %.3F %.3F %.3F',
|
732 |
+
$tmp[0],
|
733 |
+
$tmp[1],
|
734 |
+
$tmp[2],
|
735 |
+
$tmp[3]
|
736 |
+
) . ']';
|
737 |
+
}
|
738 |
+
}
|
739 |
+
|
740 |
+
$res .= "\n >>\nendobj";
|
741 |
+
} else {
|
742 |
+
$res = "\n$id 0 obj\n<< /Type /Pages\n/Count 0\n>>\nendobj";
|
743 |
+
}
|
744 |
+
|
745 |
+
return $res;
|
746 |
+
}
|
747 |
+
|
748 |
+
return null;
|
749 |
+
}
|
750 |
+
|
751 |
+
/**
|
752 |
+
* define the outlines in the doc, empty for now
|
753 |
+
*
|
754 |
+
* @param $id
|
755 |
+
* @param $action
|
756 |
+
* @param string $options
|
757 |
+
* @return string|null
|
758 |
+
*/
|
759 |
+
protected function o_outlines($id, $action, $options = '')
|
760 |
+
{
|
761 |
+
if ($action !== 'new') {
|
762 |
+
$o = &$this->objects[$id];
|
763 |
+
}
|
764 |
+
|
765 |
+
switch ($action) {
|
766 |
+
case 'new':
|
767 |
+
$this->objects[$id] = array('t' => 'outlines', 'info' => array('outlines' => array()));
|
768 |
+
$this->o_catalog($this->catalogId, 'outlines', $id);
|
769 |
+
break;
|
770 |
+
|
771 |
+
case 'outline':
|
772 |
+
$o['info']['outlines'][] = $options;
|
773 |
+
break;
|
774 |
+
|
775 |
+
case 'out':
|
776 |
+
if (count($o['info']['outlines'])) {
|
777 |
+
$res = "\n$id 0 obj\n<< /Type /Outlines /Kids [";
|
778 |
+
foreach ($o['info']['outlines'] as $v) {
|
779 |
+
$res .= "$v 0 R ";
|
780 |
+
}
|
781 |
+
|
782 |
+
$res .= "] /Count " . count($o['info']['outlines']) . " >>\nendobj";
|
783 |
+
} else {
|
784 |
+
$res = "\n$id 0 obj\n<< /Type /Outlines /Count 0 >>\nendobj";
|
785 |
+
}
|
786 |
+
|
787 |
+
return $res;
|
788 |
+
}
|
789 |
+
|
790 |
+
return null;
|
791 |
+
}
|
792 |
+
|
793 |
+
/**
|
794 |
+
* an object to hold the font description
|
795 |
+
*
|
796 |
+
* @param $id
|
797 |
+
* @param $action
|
798 |
+
* @param string|array $options
|
799 |
+
* @return string|null
|
800 |
+
*/
|
801 |
+
protected function o_font($id, $action, $options = '')
|
802 |
+
{
|
803 |
+
if ($action !== 'new') {
|
804 |
+
$o = &$this->objects[$id];
|
805 |
+
}
|
806 |
+
|
807 |
+
switch ($action) {
|
808 |
+
case 'new':
|
809 |
+
$this->objects[$id] = array(
|
810 |
+
't' => 'font',
|
811 |
+
'info' => array(
|
812 |
+
'name' => $options['name'],
|
813 |
+
'fontFileName' => $options['fontFileName'],
|
814 |
+
'SubType' => 'Type1'
|
815 |
+
)
|
816 |
+
);
|
817 |
+
$fontNum = $this->numFonts;
|
818 |
+
$this->objects[$id]['info']['fontNum'] = $fontNum;
|
819 |
+
|
820 |
+
// deal with the encoding and the differences
|
821 |
+
if (isset($options['differences'])) {
|
822 |
+
// then we'll need an encoding dictionary
|
823 |
+
$this->numObj++;
|
824 |
+
$this->o_fontEncoding($this->numObj, 'new', $options);
|
825 |
+
$this->objects[$id]['info']['encodingDictionary'] = $this->numObj;
|
826 |
+
} else {
|
827 |
+
if (isset($options['encoding'])) {
|
828 |
+
// we can specify encoding here
|
829 |
+
switch ($options['encoding']) {
|
830 |
+
case 'WinAnsiEncoding':
|
831 |
+
case 'MacRomanEncoding':
|
832 |
+
case 'MacExpertEncoding':
|
833 |
+
$this->objects[$id]['info']['encoding'] = $options['encoding'];
|
834 |
+
break;
|
835 |
+
|
836 |
+
case 'none':
|
837 |
+
break;
|
838 |
+
|
839 |
+
default:
|
840 |
+
$this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
|
841 |
+
break;
|
842 |
+
}
|
843 |
+
} else {
|
844 |
+
$this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
|
845 |
+
}
|
846 |
+
}
|
847 |
+
|
848 |
+
if ($this->fonts[$options['fontFileName']]['isUnicode']) {
|
849 |
+
// For Unicode fonts, we need to incorporate font data into
|
850 |
+
// sub-sections that are linked from the primary font section.
|
851 |
+
// Look at o_fontGIDtoCID and o_fontDescendentCID functions
|
852 |
+
// for more information.
|
853 |
+
//
|
854 |
+
// All of this code is adapted from the excellent changes made to
|
855 |
+
// transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
|
856 |
+
|
857 |
+
$toUnicodeId = ++$this->numObj;
|
858 |
+
$this->o_toUnicode($toUnicodeId, 'new');
|
859 |
+
$this->objects[$id]['info']['toUnicode'] = $toUnicodeId;
|
860 |
+
|
861 |
+
$cidFontId = ++$this->numObj;
|
862 |
+
$this->o_fontDescendentCID($cidFontId, 'new', $options);
|
863 |
+
$this->objects[$id]['info']['cidFont'] = $cidFontId;
|
864 |
+
}
|
865 |
+
|
866 |
+
// also tell the pages node about the new font
|
867 |
+
$this->o_pages($this->currentNode, 'font', array('fontNum' => $fontNum, 'objNum' => $id));
|
868 |
+
break;
|
869 |
+
|
870 |
+
case 'add':
|
871 |
+
foreach ($options as $k => $v) {
|
872 |
+
switch ($k) {
|
873 |
+
case 'BaseFont':
|
874 |
+
$o['info']['name'] = $v;
|
875 |
+
break;
|
876 |
+
case 'FirstChar':
|
877 |
+
case 'LastChar':
|
878 |
+
case 'Widths':
|
879 |
+
case 'FontDescriptor':
|
880 |
+
case 'SubType':
|
881 |
+
$this->addMessage('o_font ' . $k . " : " . $v);
|
882 |
+
$o['info'][$k] = $v;
|
883 |
+
break;
|
884 |
+
}
|
885 |
+
}
|
886 |
+
|
887 |
+
// pass values down to descendent font
|
888 |
+
if (isset($o['info']['cidFont'])) {
|
889 |
+
$this->o_fontDescendentCID($o['info']['cidFont'], 'add', $options);
|
890 |
+
}
|
891 |
+
break;
|
892 |
+
|
893 |
+
case 'out':
|
894 |
+
if ($this->fonts[$this->objects[$id]['info']['fontFileName']]['isUnicode']) {
|
895 |
+
// For Unicode fonts, we need to incorporate font data into
|
896 |
+
// sub-sections that are linked from the primary font section.
|
897 |
+
// Look at o_fontGIDtoCID and o_fontDescendentCID functions
|
898 |
+
// for more information.
|
899 |
+
//
|
900 |
+
// All of this code is adapted from the excellent changes made to
|
901 |
+
// transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
|
902 |
+
|
903 |
+
$res = "\n$id 0 obj\n<</Type /Font\n/Subtype /Type0\n";
|
904 |
+
$res .= "/BaseFont /" . $o['info']['name'] . "\n";
|
905 |
+
|
906 |
+
// The horizontal identity mapping for 2-byte CIDs; may be used
|
907 |
+
// with CIDFonts using any Registry, Ordering, and Supplement values.
|
908 |
+
$res .= "/Encoding /Identity-H\n";
|
909 |
+
$res .= "/DescendantFonts [" . $o['info']['cidFont'] . " 0 R]\n";
|
910 |
+
$res .= "/ToUnicode " . $o['info']['toUnicode'] . " 0 R\n";
|
911 |
+
$res .= ">>\n";
|
912 |
+
$res .= "endobj";
|
913 |
+
} else {
|
914 |
+
$res = "\n$id 0 obj\n<< /Type /Font\n/Subtype /" . $o['info']['SubType'] . "\n";
|
915 |
+
$res .= "/Name /F" . $o['info']['fontNum'] . "\n";
|
916 |
+
$res .= "/BaseFont /" . $o['info']['name'] . "\n";
|
917 |
+
|
918 |
+
if (isset($o['info']['encodingDictionary'])) {
|
919 |
+
// then place a reference to the dictionary
|
920 |
+
$res .= "/Encoding " . $o['info']['encodingDictionary'] . " 0 R\n";
|
921 |
+
} else {
|
922 |
+
if (isset($o['info']['encoding'])) {
|
923 |
+
// use the specified encoding
|
924 |
+
$res .= "/Encoding /" . $o['info']['encoding'] . "\n";
|
925 |
+
}
|
926 |
+
}
|
927 |
+
|
928 |
+
if (isset($o['info']['FirstChar'])) {
|
929 |
+
$res .= "/FirstChar " . $o['info']['FirstChar'] . "\n";
|
930 |
+
}
|
931 |
+
|
932 |
+
if (isset($o['info']['LastChar'])) {
|
933 |
+
$res .= "/LastChar " . $o['info']['LastChar'] . "\n";
|
934 |
+
}
|
935 |
+
|
936 |
+
if (isset($o['info']['Widths'])) {
|
937 |
+
$res .= "/Widths " . $o['info']['Widths'] . " 0 R\n";
|
938 |
+
}
|
939 |
+
|
940 |
+
if (isset($o['info']['FontDescriptor'])) {
|
941 |
+
$res .= "/FontDescriptor " . $o['info']['FontDescriptor'] . " 0 R\n";
|
942 |
+
}
|
943 |
+
|
944 |
+
$res .= ">>\n";
|
945 |
+
$res .= "endobj";
|
946 |
+
}
|
947 |
+
|
948 |
+
return $res;
|
949 |
+
}
|
950 |
+
|
951 |
+
return null;
|
952 |
+
}
|
953 |
+
|
954 |
+
/**
|
955 |
+
* A toUnicode section, needed for unicode fonts
|
956 |
+
*
|
957 |
+
* @param $id
|
958 |
+
* @param $action
|
959 |
+
* @return null|string
|
960 |
+
*/
|
961 |
+
protected function o_toUnicode($id, $action)
|
962 |
+
{
|
963 |
+
switch ($action) {
|
964 |
+
case 'new':
|
965 |
+
$this->objects[$id] = array(
|
966 |
+
't' => 'toUnicode'
|
967 |
+
);
|
968 |
+
break;
|
969 |
+
case 'add':
|
970 |
+
break;
|
971 |
+
case 'out':
|
972 |
+
$ordering = '(UCS)';
|
973 |
+
$registry = '(Adobe)';
|
974 |
+
|
975 |
+
if ($this->encrypted) {
|
976 |
+
$this->encryptInit($id);
|
977 |
+
$ordering = $this->ARC4($ordering);
|
978 |
+
$registry = $this->ARC4($registry);
|
979 |
+
}
|
980 |
+
|
981 |
+
$stream = <<<EOT
|
982 |
+
/CIDInit /ProcSet findresource begin
|
983 |
+
12 dict begin
|
984 |
+
begincmap
|
985 |
+
/CIDSystemInfo
|
986 |
+
<</Registry $registry
|
987 |
+
/Ordering $ordering
|
988 |
+
/Supplement 0
|
989 |
+
>> def
|
990 |
+
/CMapName /Adobe-Identity-UCS def
|
991 |
+
/CMapType 2 def
|
992 |
+
1 begincodespacerange
|
993 |
+
<0000> <FFFF>
|
994 |
+
endcodespacerange
|
995 |
+
1 beginbfrange
|
996 |
+
<0000> <FFFF> <0000>
|
997 |
+
endbfrange
|
998 |
+
endcmap
|
999 |
+
CMapName currentdict /CMap defineresource pop
|
1000 |
+
end
|
1001 |
+
end
|
1002 |
+
EOT;
|
1003 |
+
|
1004 |
+
$res = "\n$id 0 obj\n";
|
1005 |
+
$res .= "<</Length " . mb_strlen($stream, '8bit') . " >>\n";
|
1006 |
+
$res .= "stream\n" . $stream . "\nendstream" . "\nendobj";;
|
1007 |
+
|
1008 |
+
return $res;
|
1009 |
+
}
|
1010 |
+
|
1011 |
+
return null;
|
1012 |
+
}
|
1013 |
+
|
1014 |
+
/**
|
1015 |
+
* a font descriptor, needed for including additional fonts
|
1016 |
+
*
|
1017 |
+
* @param $id
|
1018 |
+
* @param $action
|
1019 |
+
* @param string $options
|
1020 |
+
* @return null|string
|
1021 |
+
*/
|
1022 |
+
protected function o_fontDescriptor($id, $action, $options = '')
|
1023 |
+
{
|
1024 |
+
if ($action !== 'new') {
|
1025 |
+
$o = &$this->objects[$id];
|
1026 |
+
}
|
1027 |
+
|
1028 |
+
switch ($action) {
|
1029 |
+
case 'new':
|
1030 |
+
$this->objects[$id] = array('t' => 'fontDescriptor', 'info' => $options);
|
1031 |
+
break;
|
1032 |
+
|
1033 |
+
case 'out':
|
1034 |
+
$res = "\n$id 0 obj\n<< /Type /FontDescriptor\n";
|
1035 |
+
foreach ($o['info'] as $label => $value) {
|
1036 |
+
switch ($label) {
|
1037 |
+
case 'Ascent':
|
1038 |
+
case 'CapHeight':
|
1039 |
+
case 'Descent':
|
1040 |
+
case 'Flags':
|
1041 |
+
case 'ItalicAngle':
|
1042 |
+
case 'StemV':
|
1043 |
+
case 'AvgWidth':
|
1044 |
+
case 'Leading':
|
1045 |
+
case 'MaxWidth':
|
1046 |
+
case 'MissingWidth':
|
1047 |
+
case 'StemH':
|
1048 |
+
case 'XHeight':
|
1049 |
+
case 'CharSet':
|
1050 |
+
if (mb_strlen($value, '8bit')) {
|
1051 |
+
$res .= "/$label $value\n";
|
1052 |
+
}
|
1053 |
+
|
1054 |
+
break;
|
1055 |
+
case 'FontFile':
|
1056 |
+
case 'FontFile2':
|
1057 |
+
case 'FontFile3':
|
1058 |
+
$res .= "/$label $value 0 R\n";
|
1059 |
+
break;
|
1060 |
+
|
1061 |
+
case 'FontBBox':
|
1062 |
+
$res .= "/$label [$value[0] $value[1] $value[2] $value[3]]\n";
|
1063 |
+
break;
|
1064 |
+
|
1065 |
+
case 'FontName':
|
1066 |
+
$res .= "/$label /$value\n";
|
1067 |
+
break;
|
1068 |
+
}
|
1069 |
+
}
|
1070 |
+
|
1071 |
+
$res .= ">>\nendobj";
|
1072 |
+
|
1073 |
+
return $res;
|
1074 |
+
}
|
1075 |
+
|
1076 |
+
return null;
|
1077 |
+
}
|
1078 |
+
|
1079 |
+
/**
|
1080 |
+
* the font encoding
|
1081 |
+
*
|
1082 |
+
* @param $id
|
1083 |
+
* @param $action
|
1084 |
+
* @param string $options
|
1085 |
+
* @return null|string
|
1086 |
+
*/
|
1087 |
+
protected function o_fontEncoding($id, $action, $options = '')
|
1088 |
+
{
|
1089 |
+
if ($action !== 'new') {
|
1090 |
+
$o = &$this->objects[$id];
|
1091 |
+
}
|
1092 |
+
|
1093 |
+
switch ($action) {
|
1094 |
+
case 'new':
|
1095 |
+
// the options array should contain 'differences' and maybe 'encoding'
|
1096 |
+
$this->objects[$id] = array('t' => 'fontEncoding', 'info' => $options);
|
1097 |
+
break;
|
1098 |
+
|
1099 |
+
case 'out':
|
1100 |
+
$res = "\n$id 0 obj\n<< /Type /Encoding\n";
|
1101 |
+
if (!isset($o['info']['encoding'])) {
|
1102 |
+
$o['info']['encoding'] = 'WinAnsiEncoding';
|
1103 |
+
}
|
1104 |
+
|
1105 |
+
if ($o['info']['encoding'] !== 'none') {
|
1106 |
+
$res .= "/BaseEncoding /" . $o['info']['encoding'] . "\n";
|
1107 |
+
}
|
1108 |
+
|
1109 |
+
$res .= "/Differences \n[";
|
1110 |
+
|
1111 |
+
$onum = -100;
|
1112 |
+
|
1113 |
+
foreach ($o['info']['differences'] as $num => $label) {
|
1114 |
+
if ($num != $onum + 1) {
|
1115 |
+
// we cannot make use of consecutive numbering
|
1116 |
+
$res .= "\n$num /$label";
|
1117 |
+
} else {
|
1118 |
+
$res .= " /$label";
|
1119 |
+
}
|
1120 |
+
|
1121 |
+
$onum = $num;
|
1122 |
+
}
|
1123 |
+
|
1124 |
+
$res .= "\n]\n>>\nendobj";
|
1125 |
+
|
1126 |
+
return $res;
|
1127 |
+
}
|
1128 |
+
|
1129 |
+
return null;
|
1130 |
+
}
|
1131 |
+
|
1132 |
+
/**
|
1133 |
+
* a descendent cid font, needed for unicode fonts
|
1134 |
+
*
|
1135 |
+
* @param $id
|
1136 |
+
* @param $action
|
1137 |
+
* @param string|array $options
|
1138 |
+
* @return null|string
|
1139 |
+
*/
|
1140 |
+
protected function o_fontDescendentCID($id, $action, $options = '')
|
1141 |
+
{
|
1142 |
+
if ($action !== 'new') {
|
1143 |
+
$o = &$this->objects[$id];
|
1144 |
+
}
|
1145 |
+
|
1146 |
+
switch ($action) {
|
1147 |
+
case 'new':
|
1148 |
+
$this->objects[$id] = array('t' => 'fontDescendentCID', 'info' => $options);
|
1149 |
+
|
1150 |
+
// we need a CID system info section
|
1151 |
+
$cidSystemInfoId = ++$this->numObj;
|
1152 |
+
$this->o_cidSystemInfo($cidSystemInfoId, 'new');
|
1153 |
+
$this->objects[$id]['info']['cidSystemInfo'] = $cidSystemInfoId;
|
1154 |
+
|
1155 |
+
// and a CID to GID map
|
1156 |
+
$cidToGidMapId = ++$this->numObj;
|
1157 |
+
$this->o_fontGIDtoCIDMap($cidToGidMapId, 'new', $options);
|
1158 |
+
$this->objects[$id]['info']['cidToGidMap'] = $cidToGidMapId;
|
1159 |
+
break;
|
1160 |
+
|
1161 |
+
case 'add':
|
1162 |
+
foreach ($options as $k => $v) {
|
1163 |
+
switch ($k) {
|
1164 |
+
case 'BaseFont':
|
1165 |
+
$o['info']['name'] = $v;
|
1166 |
+
break;
|
1167 |
+
|
1168 |
+
case 'FirstChar':
|
1169 |
+
case 'LastChar':
|
1170 |
+
case 'MissingWidth':
|
1171 |
+
case 'FontDescriptor':
|
1172 |
+
case 'SubType':
|
1173 |
+
$this->addMessage("o_fontDescendentCID $k : $v");
|
1174 |
+
$o['info'][$k] = $v;
|
1175 |
+
break;
|
1176 |
+
}
|
1177 |
+
}
|
1178 |
+
|
1179 |
+
// pass values down to cid to gid map
|
1180 |
+
$this->o_fontGIDtoCIDMap($o['info']['cidToGidMap'], 'add', $options);
|
1181 |
+
break;
|
1182 |
+
|
1183 |
+
case 'out':
|
1184 |
+
$res = "\n$id 0 obj\n";
|
1185 |
+
$res .= "<</Type /Font\n";
|
1186 |
+
$res .= "/Subtype /CIDFontType2\n";
|
1187 |
+
$res .= "/BaseFont /" . $o['info']['name'] . "\n";
|
1188 |
+
$res .= "/CIDSystemInfo " . $o['info']['cidSystemInfo'] . " 0 R\n";
|
1189 |
+
// if (isset($o['info']['FirstChar'])) {
|
1190 |
+
// $res.= "/FirstChar ".$o['info']['FirstChar']."\n";
|
1191 |
+
// }
|
1192 |
+
|
1193 |
+
// if (isset($o['info']['LastChar'])) {
|
1194 |
+
// $res.= "/LastChar ".$o['info']['LastChar']."\n";
|
1195 |
+
// }
|
1196 |
+
if (isset($o['info']['FontDescriptor'])) {
|
1197 |
+
$res .= "/FontDescriptor " . $o['info']['FontDescriptor'] . " 0 R\n";
|
1198 |
+
}
|
1199 |
+
|
1200 |
+
if (isset($o['info']['MissingWidth'])) {
|
1201 |
+
$res .= "/DW " . $o['info']['MissingWidth'] . "\n";
|
1202 |
+
}
|
1203 |
+
|
1204 |
+
if (isset($o['info']['fontFileName']) && isset($this->fonts[$o['info']['fontFileName']]['CIDWidths'])) {
|
1205 |
+
$cid_widths = &$this->fonts[$o['info']['fontFileName']]['CIDWidths'];
|
1206 |
+
$w = '';
|
1207 |
+
foreach ($cid_widths as $cid => $width) {
|
1208 |
+
$w .= "$cid [$width] ";
|
1209 |
+
}
|
1210 |
+
$res .= "/W [$w]\n";
|
1211 |
+
}
|
1212 |
+
|
1213 |
+
$res .= "/CIDToGIDMap " . $o['info']['cidToGidMap'] . " 0 R\n";
|
1214 |
+
$res .= ">>\n";
|
1215 |
+
$res .= "endobj";
|
1216 |
+
|
1217 |
+
return $res;
|
1218 |
+
}
|
1219 |
+
|
1220 |
+
return null;
|
1221 |
+
}
|
1222 |
+
|
1223 |
+
/**
|
1224 |
+
* CID system info section, needed for unicode fonts
|
1225 |
+
*
|
1226 |
+
* @param $id
|
1227 |
+
* @param $action
|
1228 |
+
* @return null|string
|
1229 |
+
*/
|
1230 |
+
protected function o_cidSystemInfo($id, $action)
|
1231 |
+
{
|
1232 |
+
switch ($action) {
|
1233 |
+
case 'new':
|
1234 |
+
$this->objects[$id] = array(
|
1235 |
+
't' => 'cidSystemInfo'
|
1236 |
+
);
|
1237 |
+
break;
|
1238 |
+
case 'add':
|
1239 |
+
break;
|
1240 |
+
case 'out':
|
1241 |
+
$ordering = '(UCS)';
|
1242 |
+
$registry = '(Adobe)';
|
1243 |
+
|
1244 |
+
if ($this->encrypted) {
|
1245 |
+
$this->encryptInit($id);
|
1246 |
+
$ordering = $this->ARC4($ordering);
|
1247 |
+
$registry = $this->ARC4($registry);
|
1248 |
+
}
|
1249 |
+
|
1250 |
+
|
1251 |
+
$res = "\n$id 0 obj\n";
|
1252 |
+
|
1253 |
+
$res .= '<</Registry ' . $registry . "\n"; // A string identifying an issuer of character collections
|
1254 |
+
$res .= '/Ordering ' . $ordering . "\n"; // A string that uniquely names a character collection issued by a specific registry
|
1255 |
+
$res .= "/Supplement 0\n"; // The supplement number of the character collection.
|
1256 |
+
$res .= ">>";
|
1257 |
+
|
1258 |
+
$res .= "\nendobj";;
|
1259 |
+
|
1260 |
+
return $res;
|
1261 |
+
}
|
1262 |
+
|
1263 |
+
return null;
|
1264 |
+
}
|
1265 |
+
|
1266 |
+
/**
|
1267 |
+
* a font glyph to character map, needed for unicode fonts
|
1268 |
+
*
|
1269 |
+
* @param $id
|
1270 |
+
* @param $action
|
1271 |
+
* @param string $options
|
1272 |
+
* @return null|string
|
1273 |
+
*/
|
1274 |
+
protected function o_fontGIDtoCIDMap($id, $action, $options = '')
|
1275 |
+
{
|
1276 |
+
if ($action !== 'new') {
|
1277 |
+
$o = &$this->objects[$id];
|
1278 |
+
}
|
1279 |
+
|
1280 |
+
switch ($action) {
|
1281 |
+
case 'new':
|
1282 |
+
$this->objects[$id] = array('t' => 'fontGIDtoCIDMap', 'info' => $options);
|
1283 |
+
break;
|
1284 |
+
|
1285 |
+
case 'out':
|
1286 |
+
$res = "\n$id 0 obj\n";
|
1287 |
+
$fontFileName = $o['info']['fontFileName'];
|
1288 |
+
$tmp = $this->fonts[$fontFileName]['CIDtoGID'] = base64_decode($this->fonts[$fontFileName]['CIDtoGID']);
|
1289 |
+
|
1290 |
+
$compressed = isset($this->fonts[$fontFileName]['CIDtoGID_Compressed']) &&
|
1291 |
+
$this->fonts[$fontFileName]['CIDtoGID_Compressed'];
|
1292 |
+
|
1293 |
+
if (!$compressed && isset($o['raw'])) {
|
1294 |
+
$res .= $tmp;
|
1295 |
+
} else {
|
1296 |
+
$res .= "<<";
|
1297 |
+
|
1298 |
+
if (!$compressed && $this->compressionReady && $this->options['compression']) {
|
1299 |
+
// then implement ZLIB based compression on this content stream
|
1300 |
+
$compressed = true;
|
1301 |
+
$tmp = gzcompress($tmp, 6);
|
1302 |
+
}
|
1303 |
+
if ($compressed) {
|
1304 |
+
$res .= "\n/Filter /FlateDecode";
|
1305 |
+
}
|
1306 |
+
|
1307 |
+
if ($this->encrypted) {
|
1308 |
+
$this->encryptInit($id);
|
1309 |
+
$tmp = $this->ARC4($tmp);
|
1310 |
+
}
|
1311 |
+
|
1312 |
+
$res .= "\n/Length " . mb_strlen($tmp, '8bit') . ">>\nstream\n$tmp\nendstream";
|
1313 |
+
}
|
1314 |
+
|
1315 |
+
$res .= "\nendobj";
|
1316 |
+
|
1317 |
+
return $res;
|
1318 |
+
}
|
1319 |
+
|
1320 |
+
return null;
|
1321 |
+
}
|
1322 |
+
|
1323 |
+
/**
|
1324 |
+
* the document procset, solves some problems with printing to old PS printers
|
1325 |
+
*
|
1326 |
+
* @param $id
|
1327 |
+
* @param $action
|
1328 |
+
* @param string $options
|
1329 |
+
* @return null|string
|
1330 |
+
*/
|
1331 |
+
protected function o_procset($id, $action, $options = '')
|
1332 |
+
{
|
1333 |
+
if ($action !== 'new') {
|
1334 |
+
$o = &$this->objects[$id];
|
1335 |
+
}
|
1336 |
+
|
1337 |
+
switch ($action) {
|
1338 |
+
case 'new':
|
1339 |
+
$this->objects[$id] = array('t' => 'procset', 'info' => array('PDF' => 1, 'Text' => 1));
|
1340 |
+
$this->o_pages($this->currentNode, 'procset', $id);
|
1341 |
+
$this->procsetObjectId = $id;
|
1342 |
+
break;
|
1343 |
+
|
1344 |
+
case 'add':
|
1345 |
+
// this is to add new items to the procset list, despite the fact that this is considered
|
1346 |
+
// obsolete, the items are required for printing to some postscript printers
|
1347 |
+
switch ($options) {
|
1348 |
+
case 'ImageB':
|
1349 |
+
case 'ImageC':
|
1350 |
+
case 'ImageI':
|
1351 |
+
$o['info'][$options] = 1;
|
1352 |
+
break;
|
1353 |
+
}
|
1354 |
+
break;
|
1355 |
+
|
1356 |
+
case 'out':
|
1357 |
+
$res = "\n$id 0 obj\n[";
|
1358 |
+
foreach ($o['info'] as $label => $val) {
|
1359 |
+
$res .= "/$label ";
|
1360 |
+
}
|
1361 |
+
$res .= "]\nendobj";
|
1362 |
+
|
1363 |
+
return $res;
|
1364 |
+
}
|
1365 |
+
|
1366 |
+
return null;
|
1367 |
+
}
|
1368 |
+
|
1369 |
+
/**
|
1370 |
+
* define the document information
|
1371 |
+
*
|
1372 |
+
* @param $id
|
1373 |
+
* @param $action
|
1374 |
+
* @param string $options
|
1375 |
+
* @return null|string
|
1376 |
+
*/
|
1377 |
+
protected function o_info($id, $action, $options = '')
|
1378 |
+
{
|
1379 |
+
switch ($action) {
|
1380 |
+
case 'new':
|
1381 |
+
$this->infoObject = $id;
|
1382 |
+
$date = 'D:' . @date('Ymd');
|
1383 |
+
$this->objects[$id] = array(
|
1384 |
+
't' => 'info',
|
1385 |
+
'info' => array(
|
1386 |
+
'Producer' => 'CPDF (dompdf)',
|
1387 |
+
'CreationDate' => $date
|
1388 |
+
)
|
1389 |
+
);
|
1390 |
+
break;
|
1391 |
+
case 'Title':
|
1392 |
+
case 'Author':
|
1393 |
+
case 'Subject':
|
1394 |
+
case 'Keywords':
|
1395 |
+
case 'Creator':
|
1396 |
+
case 'Producer':
|
1397 |
+
case 'CreationDate':
|
1398 |
+
case 'ModDate':
|
1399 |
+
case 'Trapped':
|
1400 |
+
$this->objects[$id]['info'][$action] = $options;
|
1401 |
+
break;
|
1402 |
+
|
1403 |
+
case 'out':
|
1404 |
+
$encrypted = $this->encrypted;
|
1405 |
+
if ($encrypted) {
|
1406 |
+
$this->encryptInit($id);
|
1407 |
+
}
|
1408 |
+
|
1409 |
+
$res = "\n$id 0 obj\n<<\n";
|
1410 |
+
$o = &$this->objects[$id];
|
1411 |
+
foreach ($o['info'] as $k => $v) {
|
1412 |
+
$res .= "/$k (";
|
1413 |
+
|
1414 |
+
// dates must be outputted as-is, without Unicode transformations
|
1415 |
+
if ($k !== 'CreationDate' && $k !== 'ModDate') {
|
1416 |
+
$v = $this->filterText($v, true, false);
|
1417 |
+
}
|
1418 |
+
|
1419 |
+
if ($encrypted) {
|
1420 |
+
$v = $this->ARC4($v);
|
1421 |
+
}
|
1422 |
+
|
1423 |
+
$res .= $v;
|
1424 |
+
$res .= ")\n";
|
1425 |
+
}
|
1426 |
+
|
1427 |
+
$res .= ">>\nendobj";
|
1428 |
+
|
1429 |
+
return $res;
|
1430 |
+
}
|
1431 |
+
|
1432 |
+
return null;
|
1433 |
+
}
|
1434 |
+
|
1435 |
+
/**
|
1436 |
+
* an action object, used to link to URLS initially
|
1437 |
+
*
|
1438 |
+
* @param $id
|
1439 |
+
* @param $action
|
1440 |
+
* @param string $options
|
1441 |
+
* @return null|string
|
1442 |
+
*/
|
1443 |
+
protected function o_action($id, $action, $options = '')
|
1444 |
+
{
|
1445 |
+
if ($action !== 'new') {
|
1446 |
+
$o = &$this->objects[$id];
|
1447 |
+
}
|
1448 |
+
|
1449 |
+
switch ($action) {
|
1450 |
+
case 'new':
|
1451 |
+
if (is_array($options)) {
|
1452 |
+
$this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => $options['type']);
|
1453 |
+
} else {
|
1454 |
+
// then assume a URI action
|
1455 |
+
$this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => 'URI');
|
1456 |
+
}
|
1457 |
+
break;
|
1458 |
+
|
1459 |
+
case 'out':
|
1460 |
+
if ($this->encrypted) {
|
1461 |
+
$this->encryptInit($id);
|
1462 |
+
}
|
1463 |
+
|
1464 |
+
$res = "\n$id 0 obj\n<< /Type /Action";
|
1465 |
+
switch ($o['type']) {
|
1466 |
+
case 'ilink':
|
1467 |
+
if (!isset($this->destinations[(string)$o['info']['label']])) {
|
1468 |
+
break;
|
1469 |
+
}
|
1470 |
+
|
1471 |
+
// there will be an 'label' setting, this is the name of the destination
|
1472 |
+
$res .= "\n/S /GoTo\n/D " . $this->destinations[(string)$o['info']['label']] . " 0 R";
|
1473 |
+
break;
|
1474 |
+
|
1475 |
+
case 'URI':
|
1476 |
+
$res .= "\n/S /URI\n/URI (";
|
1477 |
+
if ($this->encrypted) {
|
1478 |
+
$res .= $this->filterText($this->ARC4($o['info']), false, false);
|
1479 |
+
} else {
|
1480 |
+
$res .= $this->filterText($o['info'], false, false);
|
1481 |
+
}
|
1482 |
+
|
1483 |
+
$res .= ")";
|
1484 |
+
break;
|
1485 |
+
}
|
1486 |
+
|
1487 |
+
$res .= "\n>>\nendobj";
|
1488 |
+
|
1489 |
+
return $res;
|
1490 |
+
}
|
1491 |
+
|
1492 |
+
return null;
|
1493 |
+
}
|
1494 |
+
|
1495 |
+
/**
|
1496 |
+
* an annotation object, this will add an annotation to the current page.
|
1497 |
+
* initially will support just link annotations
|
1498 |
+
*
|
1499 |
+
* @param $id
|
1500 |
+
* @param $action
|
1501 |
+
* @param string $options
|
1502 |
+
* @return null|string
|
1503 |
+
*/
|
1504 |
+
protected function o_annotation($id, $action, $options = '')
|
1505 |
+
{
|
1506 |
+
if ($action !== 'new') {
|
1507 |
+
$o = &$this->objects[$id];
|
1508 |
+
}
|
1509 |
+
|
1510 |
+
switch ($action) {
|
1511 |
+
case 'new':
|
1512 |
+
// add the annotation to the current page
|
1513 |
+
$pageId = $this->currentPage;
|
1514 |
+
$this->o_page($pageId, 'annot', $id);
|
1515 |
+
|
1516 |
+
// and add the action object which is going to be required
|
1517 |
+
switch ($options['type']) {
|
1518 |
+
case 'link':
|
1519 |
+
$this->objects[$id] = array('t' => 'annotation', 'info' => $options);
|
1520 |
+
$this->numObj++;
|
1521 |
+
$this->o_action($this->numObj, 'new', $options['url']);
|
1522 |
+
$this->objects[$id]['info']['actionId'] = $this->numObj;
|
1523 |
+
break;
|
1524 |
+
|
1525 |
+
case 'ilink':
|
1526 |
+
// this is to a named internal link
|
1527 |
+
$label = $options['label'];
|
1528 |
+
$this->objects[$id] = array('t' => 'annotation', 'info' => $options);
|
1529 |
+
$this->numObj++;
|
1530 |
+
$this->o_action($this->numObj, 'new', array('type' => 'ilink', 'label' => $label));
|
1531 |
+
$this->objects[$id]['info']['actionId'] = $this->numObj;
|
1532 |
+
break;
|
1533 |
+
}
|
1534 |
+
break;
|
1535 |
+
|
1536 |
+
case 'out':
|
1537 |
+
$res = "\n$id 0 obj\n<< /Type /Annot";
|
1538 |
+
switch ($o['info']['type']) {
|
1539 |
+
case 'link':
|
1540 |
+
case 'ilink':
|
1541 |
+
$res .= "\n/Subtype /Link";
|
1542 |
+
break;
|
1543 |
+
}
|
1544 |
+
$res .= "\n/A " . $o['info']['actionId'] . " 0 R";
|
1545 |
+
$res .= "\n/Border [0 0 0]";
|
1546 |
+
$res .= "\n/H /I";
|
1547 |
+
$res .= "\n/Rect [ ";
|
1548 |
+
|
1549 |
+
foreach ($o['info']['rect'] as $v) {
|
1550 |
+
$res .= sprintf("%.4F ", $v);
|
1551 |
+
}
|
1552 |
+
|
1553 |
+
$res .= "]";
|
1554 |
+
$res .= "\n>>\nendobj";
|
1555 |
+
|
1556 |
+
return $res;
|
1557 |
+
}
|
1558 |
+
|
1559 |
+
return null;
|
1560 |
+
}
|
1561 |
+
|
1562 |
+
/**
|
1563 |
+
* a page object, it also creates a contents object to hold its contents
|
1564 |
+
*
|
1565 |
+
* @param $id
|
1566 |
+
* @param $action
|
1567 |
+
* @param string $options
|
1568 |
+
* @return null|string
|
1569 |
+
*/
|
1570 |
+
protected function o_page($id, $action, $options = '')
|
1571 |
+
{
|
1572 |
+
if ($action !== 'new') {
|
1573 |
+
$o = &$this->objects[$id];
|
1574 |
+
}
|
1575 |
+
|
1576 |
+
switch ($action) {
|
1577 |
+
case 'new':
|
1578 |
+
$this->numPages++;
|
1579 |
+
$this->objects[$id] = array(
|
1580 |
+
't' => 'page',
|
1581 |
+
'info' => array(
|
1582 |
+
'parent' => $this->currentNode,
|
1583 |
+
'pageNum' => $this->numPages,
|
1584 |
+
'mediaBox' => $this->objects[$this->currentNode]['info']['mediaBox']
|
1585 |
+
)
|
1586 |
+
);
|
1587 |
+
|
1588 |
+
if (is_array($options)) {
|
1589 |
+
// then this must be a page insertion, array should contain 'rid','pos'=[before|after]
|
1590 |
+
$options['id'] = $id;
|
1591 |
+
$this->o_pages($this->currentNode, 'page', $options);
|
1592 |
+
} else {
|
1593 |
+
$this->o_pages($this->currentNode, 'page', $id);
|
1594 |
+
}
|
1595 |
+
|
1596 |
+
$this->currentPage = $id;
|
1597 |
+
//make a contents object to go with this page
|
1598 |
+
$this->numObj++;
|
1599 |
+
$this->o_contents($this->numObj, 'new', $id);
|
1600 |
+
$this->currentContents = $this->numObj;
|
1601 |
+
$this->objects[$id]['info']['contents'] = array();
|
1602 |
+
$this->objects[$id]['info']['contents'][] = $this->numObj;
|
1603 |
+
|
1604 |
+
$match = ($this->numPages % 2 ? 'odd' : 'even');
|
1605 |
+
foreach ($this->addLooseObjects as $oId => $target) {
|
1606 |
+
if ($target === 'all' || $match === $target) {
|
1607 |
+
$this->objects[$id]['info']['contents'][] = $oId;
|
1608 |
+
}
|
1609 |
+
}
|
1610 |
+
break;
|
1611 |
+
|
1612 |
+
case 'content':
|
1613 |
+
$o['info']['contents'][] = $options;
|
1614 |
+
break;
|
1615 |
+
|
1616 |
+
case 'annot':
|
1617 |
+
// add an annotation to this page
|
1618 |
+
if (!isset($o['info']['annot'])) {
|
1619 |
+
$o['info']['annot'] = array();
|
1620 |
+
}
|
1621 |
+
|
1622 |
+
// $options should contain the id of the annotation dictionary
|
1623 |
+
$o['info']['annot'][] = $options;
|
1624 |
+
break;
|
1625 |
+
|
1626 |
+
case 'out':
|
1627 |
+
$res = "\n$id 0 obj\n<< /Type /Page";
|
1628 |
+
if (isset($o['info']['mediaBox'])) {
|
1629 |
+
$tmp = $o['info']['mediaBox'];
|
1630 |
+
$res .= "\n/MediaBox [" . sprintf(
|
1631 |
+
'%.3F %.3F %.3F %.3F',
|
1632 |
+
$tmp[0],
|
1633 |
+
$tmp[1],
|
1634 |
+
$tmp[2],
|
1635 |
+
$tmp[3]
|
1636 |
+
) . ']';
|
1637 |
+
}
|
1638 |
+
$res .= "\n/Parent " . $o['info']['parent'] . " 0 R";
|
1639 |
+
|
1640 |
+
if (isset($o['info']['annot'])) {
|
1641 |
+
$res .= "\n/Annots [";
|
1642 |
+
foreach ($o['info']['annot'] as $aId) {
|
1643 |
+
$res .= " $aId 0 R";
|
1644 |
+
}
|
1645 |
+
$res .= " ]";
|
1646 |
+
}
|
1647 |
+
|
1648 |
+
$count = count($o['info']['contents']);
|
1649 |
+
if ($count == 1) {
|
1650 |
+
$res .= "\n/Contents " . $o['info']['contents'][0] . " 0 R";
|
1651 |
+
} else {
|
1652 |
+
if ($count > 1) {
|
1653 |
+
$res .= "\n/Contents [\n";
|
1654 |
+
|
1655 |
+
// reverse the page contents so added objects are below normal content
|
1656 |
+
//foreach (array_reverse($o['info']['contents']) as $cId) {
|
1657 |
+
// Back to normal now that I've got transparency working --Benj
|
1658 |
+
foreach ($o['info']['contents'] as $cId) {
|
1659 |
+
$res .= "$cId 0 R\n";
|
1660 |
+
}
|
1661 |
+
$res .= "]";
|
1662 |
+
}
|
1663 |
+
}
|
1664 |
+
|
1665 |
+
$res .= "\n>>\nendobj";
|
1666 |
+
|
1667 |
+
return $res;
|
1668 |
+
}
|
1669 |
+
|
1670 |
+
return null;
|
1671 |
+
}
|
1672 |
+
|
1673 |
+
/**
|
1674 |
+
* the contents objects hold all of the content which appears on pages
|
1675 |
+
*
|
1676 |
+
* @param $id
|
1677 |
+
* @param $action
|
1678 |
+
* @param string|array $options
|
1679 |
+
* @return null|string
|
1680 |
+
*/
|
1681 |
+
protected function o_contents($id, $action, $options = '')
|
1682 |
+
{
|
1683 |
+
if ($action !== 'new') {
|
1684 |
+
$o = &$this->objects[$id];
|
1685 |
+
}
|
1686 |
+
|
1687 |
+
switch ($action) {
|
1688 |
+
case 'new':
|
1689 |
+
$this->objects[$id] = array('t' => 'contents', 'c' => '', 'info' => array());
|
1690 |
+
if (mb_strlen($options, '8bit') && intval($options)) {
|
1691 |
+
// then this contents is the primary for a page
|
1692 |
+
$this->objects[$id]['onPage'] = $options;
|
1693 |
+
} else {
|
1694 |
+
if ($options === 'raw') {
|
1695 |
+
// then this page contains some other type of system object
|
1696 |
+
$this->objects[$id]['raw'] = 1;
|
1697 |
+
}
|
1698 |
+
}
|
1699 |
+
break;
|
1700 |
+
|
1701 |
+
case 'add':
|
1702 |
+
// add more options to the declaration
|
1703 |
+
foreach ($options as $k => $v) {
|
1704 |
+
$o['info'][$k] = $v;
|
1705 |
+
}
|
1706 |
+
|
1707 |
+
case 'out':
|
1708 |
+
$tmp = $o['c'];
|
1709 |
+
$res = "\n$id 0 obj\n";
|
1710 |
+
|
1711 |
+
if (isset($this->objects[$id]['raw'])) {
|
1712 |
+
$res .= $tmp;
|
1713 |
+
} else {
|
1714 |
+
$res .= "<<";
|
1715 |
+
if ($this->compressionReady && $this->options['compression']) {
|
1716 |
+
// then implement ZLIB based compression on this content stream
|
1717 |
+
$res .= " /Filter /FlateDecode";
|
1718 |
+
$tmp = gzcompress($tmp, 6);
|
1719 |
+
}
|
1720 |
+
|
1721 |
+
if ($this->encrypted) {
|
1722 |
+
$this->encryptInit($id);
|
1723 |
+
$tmp = $this->ARC4($tmp);
|
1724 |
+
}
|
1725 |
+
|
1726 |
+
foreach ($o['info'] as $k => $v) {
|
1727 |
+
$res .= "\n/$k $v";
|
1728 |
+
}
|
1729 |
+
|
1730 |
+
$res .= "\n/Length " . mb_strlen($tmp, '8bit') . " >>\nstream\n$tmp\nendstream";
|
1731 |
+
}
|
1732 |
+
|
1733 |
+
$res .= "\nendobj";
|
1734 |
+
|
1735 |
+
return $res;
|
1736 |
+
}
|
1737 |
+
|
1738 |
+
return null;
|
1739 |
+
}
|
1740 |
+
|
1741 |
+
/**
|
1742 |
+
* @param $id
|
1743 |
+
* @param $action
|
1744 |
+
* @return string|null
|
1745 |
+
*/
|
1746 |
+
protected function o_embedjs($id, $action)
|
1747 |
+
{
|
1748 |
+
switch ($action) {
|
1749 |
+
case 'new':
|
1750 |
+
$this->objects[$id] = array(
|
1751 |
+
't' => 'embedjs',
|
1752 |
+
'info' => array(
|
1753 |
+
'Names' => '[(EmbeddedJS) ' . ($id + 1) . ' 0 R]'
|
1754 |
+
)
|
1755 |
+
);
|
1756 |
+
break;
|
1757 |
+
|
1758 |
+
case 'out':
|
1759 |
+
$o = &$this->objects[$id];
|
1760 |
+
$res = "\n$id 0 obj\n<< ";
|
1761 |
+
foreach ($o['info'] as $k => $v) {
|
1762 |
+
$res .= "\n/$k $v";
|
1763 |
+
}
|
1764 |
+
$res .= "\n>>\nendobj";
|
1765 |
+
|
1766 |
+
return $res;
|
1767 |
+
}
|
1768 |
+
|
1769 |
+
return null;
|
1770 |
+
}
|
1771 |
+
|
1772 |
+
/**
|
1773 |
+
* @param $id
|
1774 |
+
* @param $action
|
1775 |
+
* @param string $code
|
1776 |
+
* @return null|string
|
1777 |
+
*/
|
1778 |
+
protected function o_javascript($id, $action, $code = '')
|
1779 |
+
{
|
1780 |
+
switch ($action) {
|
1781 |
+
case 'new':
|
1782 |
+
$this->objects[$id] = array(
|
1783 |
+
't' => 'javascript',
|
1784 |
+
'info' => array(
|
1785 |
+
'S' => '/JavaScript',
|
1786 |
+
'JS' => '(' . $this->filterText($code, true, false) . ')',
|
1787 |
+
)
|
1788 |
+
);
|
1789 |
+
break;
|
1790 |
+
|
1791 |
+
case 'out':
|
1792 |
+
$o = &$this->objects[$id];
|
1793 |
+
$res = "\n$id 0 obj\n<< ";
|
1794 |
+
|
1795 |
+
foreach ($o['info'] as $k => $v) {
|
1796 |
+
$res .= "\n/$k $v";
|
1797 |
+
}
|
1798 |
+
$res .= "\n>>\nendobj";
|
1799 |
+
|
1800 |
+
return $res;
|
1801 |
+
}
|
1802 |
+
|
1803 |
+
return null;
|
1804 |
+
}
|
1805 |
+
|
1806 |
+
/**
|
1807 |
+
* an image object, will be an XObject in the document, includes description and data
|
1808 |
+
*
|
1809 |
+
* @param $id
|
1810 |
+
* @param $action
|
1811 |
+
* @param string $options
|
1812 |
+
* @return null|string
|
1813 |
+
*/
|
1814 |
+
protected function o_image($id, $action, $options = '')
|
1815 |
+
{
|
1816 |
+
switch ($action) {
|
1817 |
+
case 'new':
|
1818 |
+
// make the new object
|
1819 |
+
$this->objects[$id] = array('t' => 'image', 'data' => &$options['data'], 'info' => array());
|
1820 |
+
|
1821 |
+
$info =& $this->objects[$id]['info'];
|
1822 |
+
|
1823 |
+
$info['Type'] = '/XObject';
|
1824 |
+
$info['Subtype'] = '/Image';
|
1825 |
+
$info['Width'] = $options['iw'];
|
1826 |
+
$info['Height'] = $options['ih'];
|
1827 |
+
|
1828 |
+
if (isset($options['masked']) && $options['masked']) {
|
1829 |
+
$info['SMask'] = ($this->numObj - 1) . ' 0 R';
|
1830 |
+
}
|
1831 |
+
|
1832 |
+
if (!isset($options['type']) || $options['type'] === 'jpg') {
|
1833 |
+
if (!isset($options['channels'])) {
|
1834 |
+
$options['channels'] = 3;
|
1835 |
+
}
|
1836 |
+
|
1837 |
+
switch ($options['channels']) {
|
1838 |
+
case 1:
|
1839 |
+
$info['ColorSpace'] = '/DeviceGray';
|
1840 |
+
break;
|
1841 |
+
case 4:
|
1842 |
+
$info['ColorSpace'] = '/DeviceCMYK';
|
1843 |
+
break;
|
1844 |
+
default:
|
1845 |
+
$info['ColorSpace'] = '/DeviceRGB';
|
1846 |
+
break;
|
1847 |
+
}
|
1848 |
+
|
1849 |
+
if ($info['ColorSpace'] === '/DeviceCMYK') {
|
1850 |
+
$info['Decode'] = '[1 0 1 0 1 0 1 0]';
|
1851 |
+
}
|
1852 |
+
|
1853 |
+
$info['Filter'] = '/DCTDecode';
|
1854 |
+
$info['BitsPerComponent'] = 8;
|
1855 |
+
} else {
|
1856 |
+
if ($options['type'] === 'png') {
|
1857 |
+
$info['Filter'] = '/FlateDecode';
|
1858 |
+
$info['DecodeParms'] = '<< /Predictor 15 /Colors ' . $options['ncolor'] . ' /Columns ' . $options['iw'] . ' /BitsPerComponent ' . $options['bitsPerComponent'] . '>>';
|
1859 |
+
|
1860 |
+
if ($options['isMask']) {
|
1861 |
+
$info['ColorSpace'] = '/DeviceGray';
|
1862 |
+
} else {
|
1863 |
+
if (mb_strlen($options['pdata'], '8bit')) {
|
1864 |
+
$tmp = ' [ /Indexed /DeviceRGB ' . (mb_strlen($options['pdata'], '8bit') / 3 - 1) . ' ';
|
1865 |
+
$this->numObj++;
|
1866 |
+
$this->o_contents($this->numObj, 'new');
|
1867 |
+
$this->objects[$this->numObj]['c'] = $options['pdata'];
|
1868 |
+
$tmp .= $this->numObj . ' 0 R';
|
1869 |
+
$tmp .= ' ]';
|
1870 |
+
$info['ColorSpace'] = $tmp;
|
1871 |
+
|
1872 |
+
if (isset($options['transparency'])) {
|
1873 |
+
$transparency = $options['transparency'];
|
1874 |
+
switch ($transparency['type']) {
|
1875 |
+
case 'indexed':
|
1876 |
+
$tmp = ' [ ' . $transparency['data'] . ' ' . $transparency['data'] . '] ';
|
1877 |
+
$info['Mask'] = $tmp;
|
1878 |
+
break;
|
1879 |
+
|
1880 |
+
case 'color-key':
|
1881 |
+
$tmp = ' [ ' .
|
1882 |
+
$transparency['r'] . ' ' . $transparency['r'] .
|
1883 |
+
$transparency['g'] . ' ' . $transparency['g'] .
|
1884 |
+
$transparency['b'] . ' ' . $transparency['b'] .
|
1885 |
+
' ] ';
|
1886 |
+
$info['Mask'] = $tmp;
|
1887 |
+
break;
|
1888 |
+
}
|
1889 |
+
}
|
1890 |
+
} else {
|
1891 |
+
if (isset($options['transparency'])) {
|
1892 |
+
$transparency = $options['transparency'];
|
1893 |
+
|
1894 |
+
switch ($transparency['type']) {
|
1895 |
+
case 'indexed':
|
1896 |
+
$tmp = ' [ ' . $transparency['data'] . ' ' . $transparency['data'] . '] ';
|
1897 |
+
$info['Mask'] = $tmp;
|
1898 |
+
break;
|
1899 |
+
|
1900 |
+
case 'color-key':
|
1901 |
+
$tmp = ' [ ' .
|
1902 |
+
$transparency['r'] . ' ' . $transparency['r'] . ' ' .
|
1903 |
+
$transparency['g'] . ' ' . $transparency['g'] . ' ' .
|
1904 |
+
$transparency['b'] . ' ' . $transparency['b'] .
|
1905 |
+
' ] ';
|
1906 |
+
$info['Mask'] = $tmp;
|
1907 |
+
break;
|
1908 |
+
}
|
1909 |
+
}
|
1910 |
+
$info['ColorSpace'] = '/' . $options['color'];
|
1911 |
+
}
|
1912 |
+
}
|
1913 |
+
|
1914 |
+
$info['BitsPerComponent'] = $options['bitsPerComponent'];
|
1915 |
+
}
|
1916 |
+
}
|
1917 |
+
|
1918 |
+
// assign it a place in the named resource dictionary as an external object, according to
|
1919 |
+
// the label passed in with it.
|
1920 |
+
$this->o_pages($this->currentNode, 'xObject', array('label' => $options['label'], 'objNum' => $id));
|
1921 |
+
|
1922 |
+
// also make sure that we have the right procset object for it.
|
1923 |
+
$this->o_procset($this->procsetObjectId, 'add', 'ImageC');
|
1924 |
+
break;
|
1925 |
+
|
1926 |
+
case 'out':
|
1927 |
+
$o = &$this->objects[$id];
|
1928 |
+
$tmp = &$o['data'];
|
1929 |
+
$res = "\n$id 0 obj\n<<";
|
1930 |
+
|
1931 |
+
foreach ($o['info'] as $k => $v) {
|
1932 |
+
$res .= "\n/$k $v";
|
1933 |
+
}
|
1934 |
+
|
1935 |
+
if ($this->encrypted) {
|
1936 |
+
$this->encryptInit($id);
|
1937 |
+
$tmp = $this->ARC4($tmp);
|
1938 |
+
}
|
1939 |
+
|
1940 |
+
$res .= "\n/Length " . mb_strlen($tmp, '8bit') . ">>\nstream\n$tmp\nendstream\nendobj";
|
1941 |
+
|
1942 |
+
return $res;
|
1943 |
+
}
|
1944 |
+
|
1945 |
+
return null;
|
1946 |
+
}
|
1947 |
+
|
1948 |
+
/**
|
1949 |
+
* graphics state object
|
1950 |
+
*
|
1951 |
+
* @param $id
|
1952 |
+
* @param $action
|
1953 |
+
* @param string $options
|
1954 |
+
* @return null|string
|
1955 |
+
*/
|
1956 |
+
protected function o_extGState($id, $action, $options = "")
|
1957 |
+
{
|
1958 |
+
static $valid_params = array(
|
1959 |
+
"LW",
|
1960 |
+
"LC",
|
1961 |
+
"LC",
|
1962 |
+
"LJ",
|
1963 |
+
"ML",
|
1964 |
+
"D",
|
1965 |
+
"RI",
|
1966 |
+
"OP",
|
1967 |
+
"op",
|
1968 |
+
"OPM",
|
1969 |
+
"Font",
|
1970 |
+
"BG",
|
1971 |
+
"BG2",
|
1972 |
+
"UCR",
|
1973 |
+
"TR",
|
1974 |
+
"TR2",
|
1975 |
+
"HT",
|
1976 |
+
"FL",
|
1977 |
+
"SM",
|
1978 |
+
"SA",
|
1979 |
+
"BM",
|
1980 |
+
"SMask",
|
1981 |
+
"CA",
|
1982 |
+
"ca",
|
1983 |
+
"AIS",
|
1984 |
+
"TK"
|
1985 |
+
);
|
1986 |
+
|
1987 |
+
switch ($action) {
|
1988 |
+
case "new":
|
1989 |
+
$this->objects[$id] = array('t' => 'extGState', 'info' => $options);
|
1990 |
+
|
1991 |
+
// Tell the pages about the new resource
|
1992 |
+
$this->numStates++;
|
1993 |
+
$this->o_pages($this->currentNode, 'extGState', array("objNum" => $id, "stateNum" => $this->numStates));
|
1994 |
+
break;
|
1995 |
+
|
1996 |
+
case "out":
|
1997 |
+
$o = &$this->objects[$id];
|
1998 |
+
$res = "\n$id 0 obj\n<< /Type /ExtGState\n";
|
1999 |
+
|
2000 |
+
foreach ($o["info"] as $k => $v) {
|
2001 |
+
if (!in_array($k, $valid_params)) {
|
2002 |
+
continue;
|
2003 |
+
}
|
2004 |
+
$res .= "/$k $v\n";
|
2005 |
+
}
|
2006 |
+
|
2007 |
+
$res .= ">>\nendobj";
|
2008 |
+
|
2009 |
+
return $res;
|
2010 |
+
}
|
2011 |
+
|
2012 |
+
return null;
|
2013 |
+
}
|
2014 |
+
|
2015 |
+
/**
|
2016 |
+
* encryption object.
|
2017 |
+
*
|
2018 |
+
* @param $id
|
2019 |
+
* @param $action
|
2020 |
+
* @param string $options
|
2021 |
+
* @return string|null
|
2022 |
+
*/
|
2023 |
+
protected function o_encryption($id, $action, $options = '')
|
2024 |
+
{
|
2025 |
+
switch ($action) {
|
2026 |
+
case 'new':
|
2027 |
+
// make the new object
|
2028 |
+
$this->objects[$id] = array('t' => 'encryption', 'info' => $options);
|
2029 |
+
$this->arc4_objnum = $id;
|
2030 |
+
break;
|
2031 |
+
|
2032 |
+
case 'keys':
|
2033 |
+
// figure out the additional parameters required
|
2034 |
+
$pad = chr(0x28) . chr(0xBF) . chr(0x4E) . chr(0x5E) . chr(0x4E) . chr(0x75) . chr(0x8A) . chr(0x41)
|
2035 |
+
. chr(0x64) . chr(0x00) . chr(0x4E) . chr(0x56) . chr(0xFF) . chr(0xFA) . chr(0x01) . chr(0x08)
|
2036 |
+
. chr(0x2E) . chr(0x2E) . chr(0x00) . chr(0xB6) . chr(0xD0) . chr(0x68) . chr(0x3E) . chr(0x80)
|
2037 |
+
. chr(0x2F) . chr(0x0C) . chr(0xA9) . chr(0xFE) . chr(0x64) . chr(0x53) . chr(0x69) . chr(0x7A);
|
2038 |
+
|
2039 |
+
$info = $this->objects[$id]['info'];
|
2040 |
+
|
2041 |
+
$len = mb_strlen($info['owner'], '8bit');
|
2042 |
+
|
2043 |
+
if ($len > 32) {
|
2044 |
+
$owner = substr($info['owner'], 0, 32);
|
2045 |
+
} else {
|
2046 |
+
if ($len < 32) {
|
2047 |
+
$owner = $info['owner'] . substr($pad, 0, 32 - $len);
|
2048 |
+
} else {
|
2049 |
+
$owner = $info['owner'];
|
2050 |
+
}
|
2051 |
+
}
|
2052 |
+
|
2053 |
+
$len = mb_strlen($info['user'], '8bit');
|
2054 |
+
if ($len > 32) {
|
2055 |
+
$user = substr($info['user'], 0, 32);
|
2056 |
+
} else {
|
2057 |
+
if ($len < 32) {
|
2058 |
+
$user = $info['user'] . substr($pad, 0, 32 - $len);
|
2059 |
+
} else {
|
2060 |
+
$user = $info['user'];
|
2061 |
+
}
|
2062 |
+
}
|
2063 |
+
|
2064 |
+
$tmp = $this->md5_16($owner);
|
2065 |
+
$okey = substr($tmp, 0, 5);
|
2066 |
+
$this->ARC4_init($okey);
|
2067 |
+
$ovalue = $this->ARC4($user);
|
2068 |
+
$this->objects[$id]['info']['O'] = $ovalue;
|
2069 |
+
|
2070 |
+
// now make the u value, phew.
|
2071 |
+
$tmp = $this->md5_16(
|
2072 |
+
$user . $ovalue . chr($info['p']) . chr(255) . chr(255) . chr(255) . hex2bin($this->fileIdentifier)
|
2073 |
+
);
|
2074 |
+
|
2075 |
+
$ukey = substr($tmp, 0, 5);
|
2076 |
+
$this->ARC4_init($ukey);
|
2077 |
+
$this->encryptionKey = $ukey;
|
2078 |
+
$this->encrypted = true;
|
2079 |
+
$uvalue = $this->ARC4($pad);
|
2080 |
+
$this->objects[$id]['info']['U'] = $uvalue;
|
2081 |
+
// initialize the arc4 array
|
2082 |
+
break;
|
2083 |
+
|
2084 |
+
case 'out':
|
2085 |
+
$o = &$this->objects[$id];
|
2086 |
+
|
2087 |
+
$res = "\n$id 0 obj\n<<";
|
2088 |
+
$res .= "\n/Filter /Standard";
|
2089 |
+
$res .= "\n/V 1";
|
2090 |
+
$res .= "\n/R 2";
|
2091 |
+
$res .= "\n/O (" . $this->filterText($o['info']['O'], false, false) . ')';
|
2092 |
+
$res .= "\n/U (" . $this->filterText($o['info']['U'], false, false) . ')';
|
2093 |
+
// and the p-value needs to be converted to account for the twos-complement approach
|
2094 |
+
$o['info']['p'] = (($o['info']['p'] ^ 255) + 1) * -1;
|
2095 |
+
$res .= "\n/P " . ($o['info']['p']);
|
2096 |
+
$res .= "\n>>\nendobj";
|
2097 |
+
|
2098 |
+
return $res;
|
2099 |
+
}
|
2100 |
+
|
2101 |
+
return null;
|
2102 |
+
}
|
2103 |
+
|
2104 |
+
/**
|
2105 |
+
* ARC4 functions
|
2106 |
+
* A series of function to implement ARC4 encoding in PHP
|
2107 |
+
*/
|
2108 |
+
|
2109 |
+
/**
|
2110 |
+
* calculate the 16 byte version of the 128 bit md5 digest of the string
|
2111 |
+
*
|
2112 |
+
* @param $string
|
2113 |
+
* @return string
|
2114 |
+
*/
|
2115 |
+
function md5_16($string)
|
2116 |
+
{
|
2117 |
+
$tmp = md5($string);
|
2118 |
+
$out = '';
|
2119 |
+
for ($i = 0; $i <= 30; $i = $i + 2) {
|
2120 |
+
$out .= chr(hexdec(substr($tmp, $i, 2)));
|
2121 |
+
}
|
2122 |
+
|
2123 |
+
return $out;
|
2124 |
+
}
|
2125 |
+
|
2126 |
+
/**
|
2127 |
+
* initialize the encryption for processing a particular object
|
2128 |
+
*
|
2129 |
+
* @param $id
|
2130 |
+
*/
|
2131 |
+
function encryptInit($id)
|
2132 |
+
{
|
2133 |
+
$tmp = $this->encryptionKey;
|
2134 |
+
$hex = dechex($id);
|
2135 |
+
if (mb_strlen($hex, '8bit') < 6) {
|
2136 |
+
$hex = substr('000000', 0, 6 - mb_strlen($hex, '8bit')) . $hex;
|
2137 |
+
}
|
2138 |
+
$tmp .= chr(hexdec(substr($hex, 4, 2)))
|
2139 |
+
. chr(hexdec(substr($hex, 2, 2)))
|
2140 |
+
. chr(hexdec(substr($hex, 0, 2)))
|
2141 |
+
. chr(0)
|
2142 |
+
. chr(0)
|
2143 |
+
;
|
2144 |
+
$key = $this->md5_16($tmp);
|
2145 |
+
$this->ARC4_init(substr($key, 0, 10));
|
2146 |
+
}
|
2147 |
+
|
2148 |
+
/**
|
2149 |
+
* initialize the ARC4 encryption
|
2150 |
+
*
|
2151 |
+
* @param string $key
|
2152 |
+
*/
|
2153 |
+
function ARC4_init($key = '')
|
2154 |
+
{
|
2155 |
+
$this->arc4 = '';
|
2156 |
+
|
2157 |
+
// setup the control array
|
2158 |
+
if (mb_strlen($key, '8bit') == 0) {
|
2159 |
+
return;
|
2160 |
+
}
|
2161 |
+
|
2162 |
+
$k = '';
|
2163 |
+
while (mb_strlen($k, '8bit') < 256) {
|
2164 |
+
$k .= $key;
|
2165 |
+
}
|
2166 |
+
|
2167 |
+
$k = substr($k, 0, 256);
|
2168 |
+
for ($i = 0; $i < 256; $i++) {
|
2169 |
+
$this->arc4 .= chr($i);
|
2170 |
+
}
|
2171 |
+
|
2172 |
+
$j = 0;
|
2173 |
+
|
2174 |
+
for ($i = 0; $i < 256; $i++) {
|
2175 |
+
$t = $this->arc4[$i];
|
2176 |
+
$j = ($j + ord($t) + ord($k[$i])) % 256;
|
2177 |
+
$this->arc4[$i] = $this->arc4[$j];
|
2178 |
+
$this->arc4[$j] = $t;
|
2179 |
+
}
|
2180 |
+
}
|
2181 |
+
|
2182 |
+
/**
|
2183 |
+
* ARC4 encrypt a text string
|
2184 |
+
*
|
2185 |
+
* @param $text
|
2186 |
+
* @return string
|
2187 |
+
*/
|
2188 |
+
function ARC4($text)
|
2189 |
+
{
|
2190 |
+
$len = mb_strlen($text, '8bit');
|
2191 |
+
$a = 0;
|
2192 |
+
$b = 0;
|
2193 |
+
$c = $this->arc4;
|
2194 |
+
$out = '';
|
2195 |
+
for ($i = 0; $i < $len; $i++) {
|
2196 |
+
$a = ($a + 1) % 256;
|
2197 |
+
$t = $c[$a];
|
2198 |
+
$b = ($b + ord($t)) % 256;
|
2199 |
+
$c[$a] = $c[$b];
|
2200 |
+
$c[$b] = $t;
|
2201 |
+
$k = ord($c[(ord($c[$a]) + ord($c[$b])) % 256]);
|
2202 |
+
$out .= chr(ord($text[$i]) ^ $k);
|
2203 |
+
}
|
2204 |
+
|
2205 |
+
return $out;
|
2206 |
+
}
|
2207 |
+
|
2208 |
+
/**
|
2209 |
+
* functions which can be called to adjust or add to the document
|
2210 |
+
*/
|
2211 |
+
|
2212 |
+
/**
|
2213 |
+
* add a link in the document to an external URL
|
2214 |
+
*
|
2215 |
+
* @param $url
|
2216 |
+
* @param $x0
|
2217 |
+
* @param $y0
|
2218 |
+
* @param $x1
|
2219 |
+
* @param $y1
|
2220 |
+
*/
|
2221 |
+
function addLink($url, $x0, $y0, $x1, $y1)
|
2222 |
+
{
|
2223 |
+
$this->numObj++;
|
2224 |
+
$info = array('type' => 'link', 'url' => $url, 'rect' => array($x0, $y0, $x1, $y1));
|
2225 |
+
$this->o_annotation($this->numObj, 'new', $info);
|
2226 |
+
}
|
2227 |
+
|
2228 |
+
/**
|
2229 |
+
* add a link in the document to an internal destination (ie. within the document)
|
2230 |
+
*
|
2231 |
+
* @param $label
|
2232 |
+
* @param $x0
|
2233 |
+
* @param $y0
|
2234 |
+
* @param $x1
|
2235 |
+
* @param $y1
|
2236 |
+
*/
|
2237 |
+
function addInternalLink($label, $x0, $y0, $x1, $y1)
|
2238 |
+
{
|
2239 |
+
$this->numObj++;
|
2240 |
+
$info = array('type' => 'ilink', 'label' => $label, 'rect' => array($x0, $y0, $x1, $y1));
|
2241 |
+
$this->o_annotation($this->numObj, 'new', $info);
|
2242 |
+
}
|
2243 |
+
|
2244 |
+
/**
|
2245 |
+
* set the encryption of the document
|
2246 |
+
* can be used to turn it on and/or set the passwords which it will have.
|
2247 |
+
* also the functions that the user will have are set here, such as print, modify, add
|
2248 |
+
*
|
2249 |
+
* @param string $userPass
|
2250 |
+
* @param string $ownerPass
|
2251 |
+
* @param array $pc
|
2252 |
+
*/
|
2253 |
+
function setEncryption($userPass = '', $ownerPass = '', $pc = array())
|
2254 |
+
{
|
2255 |
+
$p = bindec("11000000");
|
2256 |
+
|
2257 |
+
$options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'add' => 32);
|
2258 |
+
|
2259 |
+
foreach ($pc as $k => $v) {
|
2260 |
+
if ($v && isset($options[$k])) {
|
2261 |
+
$p += $options[$k];
|
2262 |
+
} else {
|
2263 |
+
if (isset($options[$v])) {
|
2264 |
+
$p += $options[$v];
|
2265 |
+
}
|
2266 |
+
}
|
2267 |
+
}
|
2268 |
+
|
2269 |
+
// implement encryption on the document
|
2270 |
+
if ($this->arc4_objnum == 0) {
|
2271 |
+
// then the block does not exist already, add it.
|
2272 |
+
$this->numObj++;
|
2273 |
+
if (mb_strlen($ownerPass) == 0) {
|
2274 |
+
$ownerPass = $userPass;
|
2275 |
+
}
|
2276 |
+
|
2277 |
+
$this->o_encryption($this->numObj, 'new', array('user' => $userPass, 'owner' => $ownerPass, 'p' => $p));
|
2278 |
+
}
|
2279 |
+
}
|
2280 |
+
|
2281 |
+
/**
|
2282 |
+
* should be used for internal checks, not implemented as yet
|
2283 |
+
*/
|
2284 |
+
function checkAllHere()
|
2285 |
+
{
|
2286 |
+
}
|
2287 |
+
|
2288 |
+
/**
|
2289 |
+
* return the pdf stream as a string returned from the function
|
2290 |
+
*
|
2291 |
+
* @param bool $debug
|
2292 |
+
* @return string
|
2293 |
+
*/
|
2294 |
+
function output($debug = false)
|
2295 |
+
{
|
2296 |
+
if ($debug) {
|
2297 |
+
// turn compression off
|
2298 |
+
$this->options['compression'] = false;
|
2299 |
+
}
|
2300 |
+
|
2301 |
+
if ($this->javascript) {
|
2302 |
+
$this->numObj++;
|
2303 |
+
|
2304 |
+
$js_id = $this->numObj;
|
2305 |
+
$this->o_embedjs($js_id, 'new');
|
2306 |
+
$this->o_javascript(++$this->numObj, 'new', $this->javascript);
|
2307 |
+
|
2308 |
+
$id = $this->catalogId;
|
2309 |
+
|
2310 |
+
$this->o_catalog($id, 'javascript', $js_id);
|
2311 |
+
}
|
2312 |
+
|
2313 |
+
if ($this->fileIdentifier === '') {
|
2314 |
+
$tmp = implode('', $this->objects[$this->infoObject]['info']);
|
2315 |
+
$this->fileIdentifier = md5('DOMPDF' . __FILE__ . $tmp . microtime() . mt_rand());
|
2316 |
+
}
|
2317 |
+
|
2318 |
+
if ($this->arc4_objnum) {
|
2319 |
+
$this->o_encryption($this->arc4_objnum, 'keys');
|
2320 |
+
$this->ARC4_init($this->encryptionKey);
|
2321 |
+
}
|
2322 |
+
|
2323 |
+
$this->checkAllHere();
|
2324 |
+
|
2325 |
+
$xref = array();
|
2326 |
+
$content = '%PDF-1.3';
|
2327 |
+
$pos = mb_strlen($content, '8bit');
|
2328 |
+
|
2329 |
+
foreach ($this->objects as $k => $v) {
|
2330 |
+
$tmp = 'o_' . $v['t'];
|
2331 |
+
$cont = $this->$tmp($k, 'out');
|
2332 |
+
$content .= $cont;
|
2333 |
+
$xref[] = $pos + 1; //+1 to account for \n at the start of each object
|
2334 |
+
$pos += mb_strlen($cont, '8bit');
|
2335 |
+
}
|
2336 |
+
|
2337 |
+
$content .= "\nxref\n0 " . (count($xref) + 1) . "\n0000000000 65535 f \n";
|
2338 |
+
|
2339 |
+
foreach ($xref as $p) {
|
2340 |
+
$content .= str_pad($p, 10, "0", STR_PAD_LEFT) . " 00000 n \n";
|
2341 |
+
}
|
2342 |
+
|
2343 |
+
$content .= "trailer\n<<\n" .
|
2344 |
+
'/Size ' . (count($xref) + 1) . "\n" .
|
2345 |
+
'/Root 1 0 R' . "\n" .
|
2346 |
+
'/Info ' . $this->infoObject . " 0 R\n"
|
2347 |
+
;
|
2348 |
+
|
2349 |
+
// if encryption has been applied to this document then add the marker for this dictionary
|
2350 |
+
if ($this->arc4_objnum > 0) {
|
2351 |
+
$content .= '/Encrypt ' . $this->arc4_objnum . " 0 R\n";
|
2352 |
+
}
|
2353 |
+
|
2354 |
+
$content .= '/ID[<' . $this->fileIdentifier . '><' . $this->fileIdentifier . ">]\n";
|
2355 |
+
|
2356 |
+
// account for \n added at start of xref table
|
2357 |
+
$pos++;
|
2358 |
+
|
2359 |
+
$content .= ">>\nstartxref\n$pos\n%%EOF\n";
|
2360 |
+
|
2361 |
+
return $content;
|
2362 |
+
}
|
2363 |
+
|
2364 |
+
/**
|
2365 |
+
* initialize a new document
|
2366 |
+
* if this is called on an existing document results may be unpredictable, but the existing document would be lost at minimum
|
2367 |
+
* this function is called automatically by the constructor function
|
2368 |
+
*
|
2369 |
+
* @param array $pageSize
|
2370 |
+
*/
|
2371 |
+
private function newDocument($pageSize = array(0, 0, 612, 792))
|
2372 |
+
{
|
2373 |
+
$this->numObj = 0;
|
2374 |
+
$this->objects = array();
|
2375 |
+
|
2376 |
+
$this->numObj++;
|
2377 |
+
$this->o_catalog($this->numObj, 'new');
|
2378 |
+
|
2379 |
+
$this->numObj++;
|
2380 |
+
$this->o_outlines($this->numObj, 'new');
|
2381 |
+
|
2382 |
+
$this->numObj++;
|
2383 |
+
$this->o_pages($this->numObj, 'new');
|
2384 |
+
|
2385 |
+
$this->o_pages($this->numObj, 'mediaBox', $pageSize);
|
2386 |
+
$this->currentNode = 3;
|
2387 |
+
|
2388 |
+
$this->numObj++;
|
2389 |
+
$this->o_procset($this->numObj, 'new');
|
2390 |
+
|
2391 |
+
$this->numObj++;
|
2392 |
+
$this->o_info($this->numObj, 'new');
|
2393 |
+
|
2394 |
+
$this->numObj++;
|
2395 |
+
$this->o_page($this->numObj, 'new');
|
2396 |
+
|
2397 |
+
// need to store the first page id as there is no way to get it to the user during
|
2398 |
+
// startup
|
2399 |
+
$this->firstPageId = $this->currentContents;
|
2400 |
+
}
|
2401 |
+
|
2402 |
+
/**
|
2403 |
+
* open the font file and return a php structure containing it.
|
2404 |
+
* first check if this one has been done before and saved in a form more suited to php
|
2405 |
+
* note that if a php serialized version does not exist it will try and make one, but will
|
2406 |
+
* require write access to the directory to do it... it is MUCH faster to have these serialized
|
2407 |
+
* files.
|
2408 |
+
*
|
2409 |
+
* @param $font
|
2410 |
+
*/
|
2411 |
+
private function openFont($font)
|
2412 |
+
{
|
2413 |
+
// assume that $font contains the path and file but not the extension
|
2414 |
+
$name = basename($font);
|
2415 |
+
$dir = dirname($font) . '/';
|
2416 |
+
|
2417 |
+
$fontcache = $this->fontcache;
|
2418 |
+
if ($fontcache == '') {
|
2419 |
+
$fontcache = rtrim($dir, DIRECTORY_SEPARATOR."/\\");
|
2420 |
+
}
|
2421 |
+
|
2422 |
+
//$name filename without folder and extension of font metrics
|
2423 |
+
//$dir folder of font metrics
|
2424 |
+
//$fontcache folder of runtime created php serialized version of font metrics.
|
2425 |
+
// If this is not given, the same folder as the font metrics will be used.
|
2426 |
+
// Storing and reusing serialized versions improves speed much
|
2427 |
+
|
2428 |
+
$this->addMessage("openFont: $font - $name");
|
2429 |
+
|
2430 |
+
if (!$this->isUnicode || in_array(mb_strtolower(basename($name)), self::$coreFonts)) {
|
2431 |
+
$metrics_name = "$name.afm";
|
2432 |
+
} else {
|
2433 |
+
$metrics_name = "$name.ufm";
|
2434 |
+
}
|
2435 |
+
|
2436 |
+
$cache_name = "$metrics_name.php";
|
2437 |
+
$this->addMessage("metrics: $metrics_name, cache: $cache_name");
|
2438 |
+
|
2439 |
+
if (file_exists($fontcache . '/' . $cache_name)) {
|
2440 |
+
$this->addMessage("openFont: php file exists $fontcache/$cache_name");
|
2441 |
+
$this->fonts[$font] = require($fontcache . '/' . $cache_name);
|
2442 |
+
|
2443 |
+
if (!isset($this->fonts[$font]['_version_']) || $this->fonts[$font]['_version_'] != $this->fontcacheVersion) {
|
2444 |
+
// if the font file is old, then clear it out and prepare for re-creation
|
2445 |
+
$this->addMessage('openFont: clear out, make way for new version.');
|
2446 |
+
$this->fonts[$font] = null;
|
2447 |
+
unset($this->fonts[$font]);
|
2448 |
+
}
|
2449 |
+
} else {
|
2450 |
+
$old_cache_name = "php_$metrics_name";
|
2451 |
+
if (file_exists($fontcache . '/' . $old_cache_name)) {
|
2452 |
+
$this->addMessage(
|
2453 |
+
"openFont: php file doesn't exist $fontcache/$cache_name, creating it from the old format"
|
2454 |
+
);
|
2455 |
+
$old_cache = file_get_contents($fontcache . '/' . $old_cache_name);
|
2456 |
+
file_put_contents($fontcache . '/' . $cache_name, '<?php return ' . $old_cache . ';');
|
2457 |
+
|
2458 |
+
$this->openFont($font);
|
2459 |
+
return;
|
2460 |
+
}
|
2461 |
+
}
|
2462 |
+
|
2463 |
+
if (!isset($this->fonts[$font]) && file_exists($dir . $metrics_name)) {
|
2464 |
+
// then rebuild the php_<font>.afm file from the <font>.afm file
|
2465 |
+
$this->addMessage("openFont: build php file from $dir$metrics_name");
|
2466 |
+
$data = array();
|
2467 |
+
|
2468 |
+
// 20 => 'space'
|
2469 |
+
$data['codeToName'] = array();
|
2470 |
+
|
2471 |
+
// Since we're not going to enable Unicode for the core fonts we need to use a font-based
|
2472 |
+
// setting for Unicode support rather than a global setting.
|
2473 |
+
$data['isUnicode'] = (strtolower(substr($metrics_name, -3)) !== 'afm');
|
2474 |
+
|
2475 |
+
$cidtogid = '';
|
2476 |
+
if ($data['isUnicode']) {
|
2477 |
+
$cidtogid = str_pad('', 256 * 256 * 2, "\x00");
|
2478 |
+
}
|
2479 |
+
|
2480 |
+
$file = file($dir . $metrics_name);
|
2481 |
+
|
2482 |
+
foreach ($file as $rowA) {
|
2483 |
+
$row = trim($rowA);
|
2484 |
+
$pos = strpos($row, ' ');
|
2485 |
+
|
2486 |
+
if ($pos) {
|
2487 |
+
// then there must be some keyword
|
2488 |
+
$key = substr($row, 0, $pos);
|
2489 |
+
switch ($key) {
|
2490 |
+
case 'FontName':
|
2491 |
+
case 'FullName':
|
2492 |
+
case 'FamilyName':
|
2493 |
+
case 'PostScriptName':
|
2494 |
+
case 'Weight':
|
2495 |
+
case 'ItalicAngle':
|
2496 |
+
case 'IsFixedPitch':
|
2497 |
+
case 'CharacterSet':
|
2498 |
+
case 'UnderlinePosition':
|
2499 |
+
case 'UnderlineThickness':
|
2500 |
+
case 'Version':
|
2501 |
+
case 'EncodingScheme':
|
2502 |
+
case 'CapHeight':
|
2503 |
+
case 'XHeight':
|
2504 |
+
case 'Ascender':
|
2505 |
+
case 'Descender':
|
2506 |
+
case 'StdHW':
|
2507 |
+
case 'StdVW':
|
2508 |
+
case 'StartCharMetrics':
|
2509 |
+
case 'FontHeightOffset': // OAR - Added so we can offset the height calculation of a Windows font. Otherwise it's too big.
|
2510 |
+
$data[$key] = trim(substr($row, $pos));
|
2511 |
+
break;
|
2512 |
+
|
2513 |
+
case 'FontBBox':
|
2514 |
+
$data[$key] = explode(' ', trim(substr($row, $pos)));
|
2515 |
+
break;
|
2516 |
+
|
2517 |
+
//C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ;
|
2518 |
+
case 'C': // Found in AFM files
|
2519 |
+
$bits = explode(';', trim($row));
|
2520 |
+
$dtmp = array();
|
2521 |
+
|
2522 |
+
foreach ($bits as $bit) {
|
2523 |
+
$bits2 = explode(' ', trim($bit));
|
2524 |
+
if (mb_strlen($bits2[0], '8bit') == 0) {
|
2525 |
+
continue;
|
2526 |
+
}
|
2527 |
+
|
2528 |
+
if (count($bits2) > 2) {
|
2529 |
+
$dtmp[$bits2[0]] = array();
|
2530 |
+
for ($i = 1; $i < count($bits2); $i++) {
|
2531 |
+
$dtmp[$bits2[0]][] = $bits2[$i];
|
2532 |
+
}
|
2533 |
+
} else {
|
2534 |
+
if (count($bits2) == 2) {
|
2535 |
+
$dtmp[$bits2[0]] = $bits2[1];
|
2536 |
+
}
|
2537 |
+
}
|
2538 |
+
}
|
2539 |
+
|
2540 |
+
$c = (int)$dtmp['C'];
|
2541 |
+
$n = $dtmp['N'];
|
2542 |
+
$width = floatval($dtmp['WX']);
|
2543 |
+
|
2544 |
+
if ($c >= 0) {
|
2545 |
+
if ($c != hexdec($n)) {
|
2546 |
+
$data['codeToName'][$c] = $n;
|
2547 |
+
}
|
2548 |
+
$data['C'][$c] = $width;
|
2549 |
+
} else {
|
2550 |
+
$data['C'][$n] = $width;
|
2551 |
+
}
|
2552 |
+
|
2553 |
+
if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
|
2554 |
+
$data['MissingWidth'] = $width;
|
2555 |
+
}
|
2556 |
+
|
2557 |
+
break;
|
2558 |
+
|
2559 |
+
// U 827 ; WX 0 ; N squaresubnosp ; G 675 ;
|
2560 |
+
case 'U': // Found in UFM files
|
2561 |
+
if (!$data['isUnicode']) {
|
2562 |
+
break;
|
2563 |
+
}
|
2564 |
+
|
2565 |
+
$bits = explode(';', trim($row));
|
2566 |
+
$dtmp = array();
|
2567 |
+
|
2568 |
+
foreach ($bits as $bit) {
|
2569 |
+
$bits2 = explode(' ', trim($bit));
|
2570 |
+
if (mb_strlen($bits2[0], '8bit') === 0) {
|
2571 |
+
continue;
|
2572 |
+
}
|
2573 |
+
|
2574 |
+
if (count($bits2) > 2) {
|
2575 |
+
$dtmp[$bits2[0]] = array();
|
2576 |
+
for ($i = 1; $i < count($bits2); $i++) {
|
2577 |
+
$dtmp[$bits2[0]][] = $bits2[$i];
|
2578 |
+
}
|
2579 |
+
} else {
|
2580 |
+
if (count($bits2) == 2) {
|
2581 |
+
$dtmp[$bits2[0]] = $bits2[1];
|
2582 |
+
}
|
2583 |
+
}
|
2584 |
+
}
|
2585 |
+
|
2586 |
+
$c = (int)$dtmp['U'];
|
2587 |
+
$n = $dtmp['N'];
|
2588 |
+
$glyph = $dtmp['G'];
|
2589 |
+
$width = floatval($dtmp['WX']);
|
2590 |
+
|
2591 |
+
if ($c >= 0) {
|
2592 |
+
// Set values in CID to GID map
|
2593 |
+
if ($c >= 0 && $c < 0xFFFF && $glyph) {
|
2594 |
+
$cidtogid[$c * 2] = chr($glyph >> 8);
|
2595 |
+
$cidtogid[$c * 2 + 1] = chr($glyph & 0xFF);
|
2596 |
+
}
|
2597 |
+
|
2598 |
+
if ($c != hexdec($n)) {
|
2599 |
+
$data['codeToName'][$c] = $n;
|
2600 |
+
}
|
2601 |
+
$data['C'][$c] = $width;
|
2602 |
+
} else {
|
2603 |
+
$data['C'][$n] = $width;
|
2604 |
+
}
|
2605 |
+
|
2606 |
+
if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
|
2607 |
+
$data['MissingWidth'] = $width;
|
2608 |
+
}
|
2609 |
+
|
2610 |
+
break;
|
2611 |
+
|
2612 |
+
case 'KPX':
|
2613 |
+
break; // don't include them as they are not used yet
|
2614 |
+
//KPX Adieresis yacute -40
|
2615 |
+
/*$bits = explode(' ', trim($row));
|
2616 |
+
$data['KPX'][$bits[1]][$bits[2]] = $bits[3];
|
2617 |
+
break;*/
|
2618 |
+
}
|
2619 |
+
}
|
2620 |
+
}
|
2621 |
+
|
2622 |
+
if ($this->compressionReady && $this->options['compression']) {
|
2623 |
+
// then implement ZLIB based compression on CIDtoGID string
|
2624 |
+
$data['CIDtoGID_Compressed'] = true;
|
2625 |
+
$cidtogid = gzcompress($cidtogid, 6);
|
2626 |
+
}
|
2627 |
+
$data['CIDtoGID'] = base64_encode($cidtogid);
|
2628 |
+
$data['_version_'] = $this->fontcacheVersion;
|
2629 |
+
$this->fonts[$font] = $data;
|
2630 |
+
|
2631 |
+
//Because of potential trouble with php safe mode, expect that the folder already exists.
|
2632 |
+
//If not existing, this will hit performance because of missing cached results.
|
2633 |
+
if (is_dir($fontcache) && is_writable($fontcache)) {
|
2634 |
+
file_put_contents($fontcache . '/' . $cache_name, '<?php return ' . var_export($data, true) . ';');
|
2635 |
+
}
|
2636 |
+
$data = null;
|
2637 |
+
}
|
2638 |
+
|
2639 |
+
if (!isset($this->fonts[$font])) {
|
2640 |
+
$this->addMessage("openFont: no font file found for $font. Do you need to run load_font.php?");
|
2641 |
+
}
|
2642 |
+
|
2643 |
+
//pre_r($this->messages);
|
2644 |
+
}
|
2645 |
+
|
2646 |
+
/**
|
2647 |
+
* if the font is not loaded then load it and make the required object
|
2648 |
+
* else just make it the current font
|
2649 |
+
* the encoding array can contain 'encoding'=> 'none','WinAnsiEncoding','MacRomanEncoding' or 'MacExpertEncoding'
|
2650 |
+
* note that encoding='none' will need to be used for symbolic fonts
|
2651 |
+
* and 'differences' => an array of mappings between numbers 0->255 and character names.
|
2652 |
+
*
|
2653 |
+
* @param $fontName
|
2654 |
+
* @param string $encoding
|
2655 |
+
* @param bool $set
|
2656 |
+
* @return int
|
2657 |
+
*/
|
2658 |
+
function selectFont($fontName, $encoding = '', $set = true)
|
2659 |
+
{
|
2660 |
+
$ext = substr($fontName, -4);
|
2661 |
+
if ($ext === '.afm' || $ext === '.ufm') {
|
2662 |
+
$fontName = substr($fontName, 0, mb_strlen($fontName) - 4);
|
2663 |
+
}
|
2664 |
+
|
2665 |
+
if (!isset($this->fonts[$fontName])) {
|
2666 |
+
$this->addMessage("selectFont: selecting - $fontName - $encoding, $set");
|
2667 |
+
|
2668 |
+
// load the file
|
2669 |
+
$this->openFont($fontName);
|
2670 |
+
|
2671 |
+
if (isset($this->fonts[$fontName])) {
|
2672 |
+
$this->numObj++;
|
2673 |
+
$this->numFonts++;
|
2674 |
+
|
2675 |
+
$font = &$this->fonts[$fontName];
|
2676 |
+
|
2677 |
+
$name = basename($fontName);
|
2678 |
+
$dir = dirname($fontName) . '/';
|
2679 |
+
$options = array('name' => $name, 'fontFileName' => $fontName);
|
2680 |
+
|
2681 |
+
if (is_array($encoding)) {
|
2682 |
+
// then encoding and differences might be set
|
2683 |
+
if (isset($encoding['encoding'])) {
|
2684 |
+
$options['encoding'] = $encoding['encoding'];
|
2685 |
+
}
|
2686 |
+
|
2687 |
+
if (isset($encoding['differences'])) {
|
2688 |
+
$options['differences'] = $encoding['differences'];
|
2689 |
+
}
|
2690 |
+
} else {
|
2691 |
+
if (mb_strlen($encoding, '8bit')) {
|
2692 |
+
// then perhaps only the encoding has been set
|
2693 |
+
$options['encoding'] = $encoding;
|
2694 |
+
}
|
2695 |
+
}
|
2696 |
+
|
2697 |
+
$fontObj = $this->numObj;
|
2698 |
+
$this->o_font($this->numObj, 'new', $options);
|
2699 |
+
$font['fontNum'] = $this->numFonts;
|
2700 |
+
|
2701 |
+
// if this is a '.afm' font, and there is a '.pfa' file to go with it (as there
|
2702 |
+
// should be for all non-basic fonts), then load it into an object and put the
|
2703 |
+
// references into the font object
|
2704 |
+
$basefile = $fontName;
|
2705 |
+
|
2706 |
+
$fbtype = '';
|
2707 |
+
if (file_exists("$basefile.ttf")) {
|
2708 |
+
$fbtype = 'ttf';
|
2709 |
+
} elseif (file_exists("$basefile.TTF")) {
|
2710 |
+
$fbtype = 'TTF';
|
2711 |
+
} elseif (file_exists("$basefile.pfb")) {
|
2712 |
+
$fbtype = 'pfb';
|
2713 |
+
} elseif (file_exists("$basefile.PFB")) {
|
2714 |
+
$fbtype = 'PFB';
|
2715 |
+
}
|
2716 |
+
|
2717 |
+
$fbfile = "$basefile.$fbtype";
|
2718 |
+
|
2719 |
+
// $pfbfile = substr($fontName,0,strlen($fontName)-4).'.pfb';
|
2720 |
+
// $ttffile = substr($fontName,0,strlen($fontName)-4).'.ttf';
|
2721 |
+
$this->addMessage('selectFont: checking for - ' . $fbfile);
|
2722 |
+
|
2723 |
+
// OAR - I don't understand this old check
|
2724 |
+
// if (substr($fontName, -4) === '.afm' && strlen($fbtype)) {
|
2725 |
+
if ($fbtype) {
|
2726 |
+
$adobeFontName = isset($font['PostScriptName']) ? $font['PostScriptName'] : $font['FontName'];
|
2727 |
+
// $fontObj = $this->numObj;
|
2728 |
+
$this->addMessage("selectFont: adding font file - $fbfile - $adobeFontName");
|
2729 |
+
|
2730 |
+
// find the array of font widths, and put that into an object.
|
2731 |
+
$firstChar = -1;
|
2732 |
+
$lastChar = 0;
|
2733 |
+
$widths = array();
|
2734 |
+
$cid_widths = array();
|
2735 |
+
|
2736 |
+
foreach ($font['C'] as $num => $d) {
|
2737 |
+
if (intval($num) > 0 || $num == '0') {
|
2738 |
+
if (!$font['isUnicode']) {
|
2739 |
+
// With Unicode, widths array isn't used
|
2740 |
+
if ($lastChar > 0 && $num > $lastChar + 1) {
|
2741 |
+
for ($i = $lastChar + 1; $i < $num; $i++) {
|
2742 |
+
$widths[] = 0;
|
2743 |
+
}
|
2744 |
+
}
|
2745 |
+
}
|
2746 |
+
|
2747 |
+
$widths[] = $d;
|
2748 |
+
|
2749 |
+
if ($font['isUnicode']) {
|
2750 |
+
$cid_widths[$num] = $d;
|
2751 |
+
}
|
2752 |
+
|
2753 |
+
if ($firstChar == -1) {
|
2754 |
+
$firstChar = $num;
|
2755 |
+
}
|
2756 |
+
|
2757 |
+
$lastChar = $num;
|
2758 |
+
}
|
2759 |
+
}
|
2760 |
+
|
2761 |
+
// also need to adjust the widths for the differences array
|
2762 |
+
if (isset($options['differences'])) {
|
2763 |
+
foreach ($options['differences'] as $charNum => $charName) {
|
2764 |
+
if ($charNum > $lastChar) {
|
2765 |
+
if (!$font['isUnicode']) {
|
2766 |
+
// With Unicode, widths array isn't used
|
2767 |
+
for ($i = $lastChar + 1; $i <= $charNum; $i++) {
|
2768 |
+
$widths[] = 0;
|
2769 |
+
}
|
2770 |
+
}
|
2771 |
+
|
2772 |
+
$lastChar = $charNum;
|
2773 |
+
}
|
2774 |
+
|
2775 |
+
if (isset($font['C'][$charName])) {
|
2776 |
+
$widths[$charNum - $firstChar] = $font['C'][$charName];
|
2777 |
+
if ($font['isUnicode']) {
|
2778 |
+
$cid_widths[$charName] = $font['C'][$charName];
|
2779 |
+
}
|
2780 |
+
}
|
2781 |
+
}
|
2782 |
+
}
|
2783 |
+
|
2784 |
+
if ($font['isUnicode']) {
|
2785 |
+
$font['CIDWidths'] = $cid_widths;
|
2786 |
+
}
|
2787 |
+
|
2788 |
+
$this->addMessage('selectFont: FirstChar = ' . $firstChar);
|
2789 |
+
$this->addMessage('selectFont: LastChar = ' . $lastChar);
|
2790 |
+
|
2791 |
+
$widthid = -1;
|
2792 |
+
|
2793 |
+
if (!$font['isUnicode']) {
|
2794 |
+
// With Unicode, widths array isn't used
|
2795 |
+
|
2796 |
+
$this->numObj++;
|
2797 |
+
$this->o_contents($this->numObj, 'new', 'raw');
|
2798 |
+
$this->objects[$this->numObj]['c'] .= '[' . implode(' ', $widths) . ']';
|
2799 |
+
$widthid = $this->numObj;
|
2800 |
+
}
|
2801 |
+
|
2802 |
+
$missing_width = 500;
|
2803 |
+
$stemV = 70;
|
2804 |
+
|
2805 |
+
if (isset($font['MissingWidth'])) {
|
2806 |
+
$missing_width = $font['MissingWidth'];
|
2807 |
+
}
|
2808 |
+
if (isset($font['StdVW'])) {
|
2809 |
+
$stemV = $font['StdVW'];
|
2810 |
+
} else {
|
2811 |
+
if (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) {
|
2812 |
+
$stemV = 120;
|
2813 |
+
}
|
2814 |
+
}
|
2815 |
+
|
2816 |
+
// load the pfb file, and put that into an object too.
|
2817 |
+
// note that pdf supports only binary format type 1 font files, though there is a
|
2818 |
+
// simple utility to convert them from pfa to pfb.
|
2819 |
+
// FIXME: should we move font subset creation to CPDF::output? See notes in issue #750.
|
2820 |
+
if (!$this->isUnicode || strtolower($fbtype) !== 'ttf' || empty($this->stringSubsets)) {
|
2821 |
+
$data = file_get_contents($fbfile);
|
2822 |
+
} else {
|
2823 |
+
$this->stringSubsets[$fontName][] = 32; // Force space if not in yet
|
2824 |
+
|
2825 |
+
$subset = $this->stringSubsets[$fontName];
|
2826 |
+
sort($subset);
|
2827 |
+
|
2828 |
+
// Load font
|
2829 |
+
$font_obj = Font::load($fbfile);
|
2830 |
+
$font_obj->parse();
|
2831 |
+
|
2832 |
+
// Define subset
|
2833 |
+
$font_obj->setSubset($subset);
|
2834 |
+
$font_obj->reduce();
|
2835 |
+
|
2836 |
+
// Write new font
|
2837 |
+
$tmp_name = $this->tmp . "/" . basename($fbfile) . ".tmp." . uniqid();
|
2838 |
+
$font_obj->open($tmp_name, BinaryStream::modeWrite);
|
2839 |
+
$font_obj->encode(array("OS/2"));
|
2840 |
+
$font_obj->close();
|
2841 |
+
|
2842 |
+
// Parse the new font to get cid2gid and widths
|
2843 |
+
$font_obj = Font::load($tmp_name);
|
2844 |
+
|
2845 |
+
// Find Unicode char map table
|
2846 |
+
$subtable = null;
|
2847 |
+
foreach ($font_obj->getData("cmap", "subtables") as $_subtable) {
|
2848 |
+
if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
|
2849 |
+
$subtable = $_subtable;
|
2850 |
+
break;
|
2851 |
+
}
|
2852 |
+
}
|
2853 |
+
|
2854 |
+
if ($subtable) {
|
2855 |
+
$glyphIndexArray = $subtable["glyphIndexArray"];
|
2856 |
+
$hmtx = $font_obj->getData("hmtx");
|
2857 |
+
|
2858 |
+
unset($glyphIndexArray[0xFFFF]);
|
2859 |
+
|
2860 |
+
$cidtogid = str_pad('', max(array_keys($glyphIndexArray)) * 2 + 1, "\x00");
|
2861 |
+
$font['CIDWidths'] = array();
|
2862 |
+
foreach ($glyphIndexArray as $cid => $gid) {
|
2863 |
+
if ($cid >= 0 && $cid < 0xFFFF && $gid) {
|
2864 |
+
$cidtogid[$cid * 2] = chr($gid >> 8);
|
2865 |
+
$cidtogid[$cid * 2 + 1] = chr($gid & 0xFF);
|
2866 |
+
}
|
2867 |
+
|
2868 |
+
$width = $font_obj->normalizeFUnit(isset($hmtx[$gid]) ? $hmtx[$gid][0] : $hmtx[0][0]);
|
2869 |
+
$font['CIDWidths'][$cid] = $width;
|
2870 |
+
}
|
2871 |
+
|
2872 |
+
$font['CIDtoGID'] = base64_encode(gzcompress($cidtogid));
|
2873 |
+
$font['CIDtoGID_Compressed'] = true;
|
2874 |
+
|
2875 |
+
$data = file_get_contents($tmp_name);
|
2876 |
+
} else {
|
2877 |
+
$data = file_get_contents($fbfile);
|
2878 |
+
}
|
2879 |
+
|
2880 |
+
$font_obj->close();
|
2881 |
+
unlink($tmp_name);
|
2882 |
+
}
|
2883 |
+
|
2884 |
+
// create the font descriptor
|
2885 |
+
$this->numObj++;
|
2886 |
+
$fontDescriptorId = $this->numObj;
|
2887 |
+
|
2888 |
+
$this->numObj++;
|
2889 |
+
$pfbid = $this->numObj;
|
2890 |
+
|
2891 |
+
// determine flags (more than a little flakey, hopefully will not matter much)
|
2892 |
+
$flags = 0;
|
2893 |
+
|
2894 |
+
if ($font['ItalicAngle'] != 0) {
|
2895 |
+
$flags += pow(2, 6);
|
2896 |
+
}
|
2897 |
+
|
2898 |
+
if ($font['IsFixedPitch'] === 'true') {
|
2899 |
+
$flags += 1;
|
2900 |
+
}
|
2901 |
+
|
2902 |
+
$flags += pow(2, 5); // assume non-sybolic
|
2903 |
+
$list = array(
|
2904 |
+
'Ascent' => 'Ascender',
|
2905 |
+
'CapHeight' => 'Ascender', //FIXME: php-font-lib is not grabbing this value, so we'll fake it and use the Ascender value // 'CapHeight'
|
2906 |
+
'MissingWidth' => 'MissingWidth',
|
2907 |
+
'Descent' => 'Descender',
|
2908 |
+
'FontBBox' => 'FontBBox',
|
2909 |
+
'ItalicAngle' => 'ItalicAngle'
|
2910 |
+
);
|
2911 |
+
$fdopt = array(
|
2912 |
+
'Flags' => $flags,
|
2913 |
+
'FontName' => $adobeFontName,
|
2914 |
+
'StemV' => $stemV
|
2915 |
+
);
|
2916 |
+
|
2917 |
+
foreach ($list as $k => $v) {
|
2918 |
+
if (isset($font[$v])) {
|
2919 |
+
$fdopt[$k] = $font[$v];
|
2920 |
+
}
|
2921 |
+
}
|
2922 |
+
|
2923 |
+
if (strtolower($fbtype) === 'pfb') {
|
2924 |
+
$fdopt['FontFile'] = $pfbid;
|
2925 |
+
} elseif (strtolower($fbtype) === 'ttf') {
|
2926 |
+
$fdopt['FontFile2'] = $pfbid;
|
2927 |
+
}
|
2928 |
+
|
2929 |
+
$this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt);
|
2930 |
+
|
2931 |
+
// embed the font program
|
2932 |
+
$this->o_contents($this->numObj, 'new');
|
2933 |
+
$this->objects[$pfbid]['c'] .= $data;
|
2934 |
+
|
2935 |
+
// determine the cruicial lengths within this file
|
2936 |
+
if (strtolower($fbtype) === 'pfb') {
|
2937 |
+
$l1 = strpos($data, 'eexec') + 6;
|
2938 |
+
$l2 = strpos($data, '00000000') - $l1;
|
2939 |
+
$l3 = mb_strlen($data, '8bit') - $l2 - $l1;
|
2940 |
+
$this->o_contents(
|
2941 |
+
$this->numObj,
|
2942 |
+
'add',
|
2943 |
+
array('Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3)
|
2944 |
+
);
|
2945 |
+
} elseif (strtolower($fbtype) == 'ttf') {
|
2946 |
+
$l1 = mb_strlen($data, '8bit');
|
2947 |
+
$this->o_contents($this->numObj, 'add', array('Length1' => $l1));
|
2948 |
+
}
|
2949 |
+
|
2950 |
+
// tell the font object about all this new stuff
|
2951 |
+
$tmp = array(
|
2952 |
+
'BaseFont' => $adobeFontName,
|
2953 |
+
'MissingWidth' => $missing_width,
|
2954 |
+
'Widths' => $widthid,
|
2955 |
+
'FirstChar' => $firstChar,
|
2956 |
+
'LastChar' => $lastChar,
|
2957 |
+
'FontDescriptor' => $fontDescriptorId
|
2958 |
+
);
|
2959 |
+
|
2960 |
+
if (strtolower($fbtype) === 'ttf') {
|
2961 |
+
$tmp['SubType'] = 'TrueType';
|
2962 |
+
}
|
2963 |
+
|
2964 |
+
$this->addMessage("adding extra info to font.($fontObj)");
|
2965 |
+
|
2966 |
+
foreach ($tmp as $fk => $fv) {
|
2967 |
+
$this->addMessage("$fk : $fv");
|
2968 |
+
}
|
2969 |
+
|
2970 |
+
$this->o_font($fontObj, 'add', $tmp);
|
2971 |
+
} else {
|
2972 |
+
$this->addMessage(
|
2973 |
+
'selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts'
|
2974 |
+
);
|
2975 |
+
}
|
2976 |
+
|
2977 |
+
// also set the differences here, note that this means that these will take effect only the
|
2978 |
+
//first time that a font is selected, else they are ignored
|
2979 |
+
if (isset($options['differences'])) {
|
2980 |
+
$font['differences'] = $options['differences'];
|
2981 |
+
}
|
2982 |
+
}
|
2983 |
+
}
|
2984 |
+
|
2985 |
+
if ($set && isset($this->fonts[$fontName])) {
|
2986 |
+
// so if for some reason the font was not set in the last one then it will not be selected
|
2987 |
+
$this->currentBaseFont = $fontName;
|
2988 |
+
|
2989 |
+
// the next lines mean that if a new font is selected, then the current text state will be
|
2990 |
+
// applied to it as well.
|
2991 |
+
$this->currentFont = $this->currentBaseFont;
|
2992 |
+
$this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
|
2993 |
+
|
2994 |
+
//$this->setCurrentFont();
|
2995 |
+
}
|
2996 |
+
|
2997 |
+
return $this->currentFontNum;
|
2998 |
+
//return $this->numObj;
|
2999 |
+
}
|
3000 |
+
|
3001 |
+
/**
|
3002 |
+
* sets up the current font, based on the font families, and the current text state
|
3003 |
+
* note that this system is quite flexible, a bold-italic font can be completely different to a
|
3004 |
+
* italic-bold font, and even bold-bold will have to be defined within the family to have meaning
|
3005 |
+
* This function is to be called whenever the currentTextState is changed, it will update
|
3006 |
+
* the currentFont setting to whatever the appropriate family one is.
|
3007 |
+
* If the user calls selectFont themselves then that will reset the currentBaseFont, and the currentFont
|
3008 |
+
* This function will change the currentFont to whatever it should be, but will not change the
|
3009 |
+
* currentBaseFont.
|
3010 |
+
*/
|
3011 |
+
private function setCurrentFont()
|
3012 |
+
{
|
3013 |
+
// if (strlen($this->currentBaseFont) == 0){
|
3014 |
+
// // then assume an initial font
|
3015 |
+
// $this->selectFont($this->defaultFont);
|
3016 |
+
// }
|
3017 |
+
// $cf = substr($this->currentBaseFont,strrpos($this->currentBaseFont,'/')+1);
|
3018 |
+
// if (strlen($this->currentTextState)
|
3019 |
+
// && isset($this->fontFamilies[$cf])
|
3020 |
+
// && isset($this->fontFamilies[$cf][$this->currentTextState])){
|
3021 |
+
// // then we are in some state or another
|
3022 |
+
// // and this font has a family, and the current setting exists within it
|
3023 |
+
// // select the font, then return it
|
3024 |
+
// $nf = substr($this->currentBaseFont,0,strrpos($this->currentBaseFont,'/')+1).$this->fontFamilies[$cf][$this->currentTextState];
|
3025 |
+
// $this->selectFont($nf,'',0);
|
3026 |
+
// $this->currentFont = $nf;
|
3027 |
+
// $this->currentFontNum = $this->fonts[$nf]['fontNum'];
|
3028 |
+
// } else {
|
3029 |
+
// // the this font must not have the right family member for the current state
|
3030 |
+
// // simply assume the base font
|
3031 |
+
$this->currentFont = $this->currentBaseFont;
|
3032 |
+
$this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
|
3033 |
+
// }
|
3034 |
+
}
|
3035 |
+
|
3036 |
+
/**
|
3037 |
+
* function for the user to find out what the ID is of the first page that was created during
|
3038 |
+
* startup - useful if they wish to add something to it later.
|
3039 |
+
*
|
3040 |
+
* @return int
|
3041 |
+
*/
|
3042 |
+
function getFirstPageId()
|
3043 |
+
{
|
3044 |
+
return $this->firstPageId;
|
3045 |
+
}
|
3046 |
+
|
3047 |
+
/**
|
3048 |
+
* add content to the currently active object
|
3049 |
+
*
|
3050 |
+
* @param $content
|
3051 |
+
*/
|
3052 |
+
private function addContent($content)
|
3053 |
+
{
|
3054 |
+
$this->objects[$this->currentContents]['c'] .= $content;
|
3055 |
+
}
|
3056 |
+
|
3057 |
+
/**
|
3058 |
+
* sets the color for fill operations
|
3059 |
+
*
|
3060 |
+
* @param $color
|
3061 |
+
* @param bool $force
|
3062 |
+
*/
|
3063 |
+
function setColor($color, $force = false)
|
3064 |
+
{
|
3065 |
+
$new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
|
3066 |
+
|
3067 |
+
if (!$force && $this->currentColor == $new_color) {
|
3068 |
+
return;
|
3069 |
+
}
|
3070 |
+
|
3071 |
+
if (isset($new_color[3])) {
|
3072 |
+
$this->currentColor = $new_color;
|
3073 |
+
$this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F k", $this->currentColor));
|
3074 |
+
} else {
|
3075 |
+
if (isset($new_color[2])) {
|
3076 |
+
$this->currentColor = $new_color;
|
3077 |
+
$this->addContent(vsprintf("\n%.3F %.3F %.3F rg", $this->currentColor));
|
3078 |
+
}
|
3079 |
+
}
|
3080 |
+
}
|
3081 |
+
|
3082 |
+
/**
|
3083 |
+
* sets the color for fill operations
|
3084 |
+
*
|
3085 |
+
* @param $fillRule
|
3086 |
+
*/
|
3087 |
+
function setFillRule($fillRule)
|
3088 |
+
{
|
3089 |
+
if (!in_array($fillRule, array("nonzero", "evenodd"))) {
|
3090 |
+
return;
|
3091 |
+
}
|
3092 |
+
|
3093 |
+
$this->fillRule = $fillRule;
|
3094 |
+
}
|
3095 |
+
|
3096 |
+
/**
|
3097 |
+
* sets the color for stroke operations
|
3098 |
+
*
|
3099 |
+
* @param $color
|
3100 |
+
* @param bool $force
|
3101 |
+
*/
|
3102 |
+
function setStrokeColor($color, $force = false)
|
3103 |
+
{
|
3104 |
+
$new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
|
3105 |
+
|
3106 |
+
if (!$force && $this->currentStrokeColor == $new_color) {
|
3107 |
+
return;
|
3108 |
+
}
|
3109 |
+
|
3110 |
+
if (isset($new_color[3])) {
|
3111 |
+
$this->currentStrokeColor = $new_color;
|
3112 |
+
$this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F K", $this->currentStrokeColor));
|
3113 |
+
} else {
|
3114 |
+
if (isset($new_color[2])) {
|
3115 |
+
$this->currentStrokeColor = $new_color;
|
3116 |
+
$this->addContent(vsprintf("\n%.3F %.3F %.3F RG", $this->currentStrokeColor));
|
3117 |
+
}
|
3118 |
+
}
|
3119 |
+
}
|
3120 |
+
|
3121 |
+
/**
|
3122 |
+
* Set the graphics state for compositions
|
3123 |
+
*
|
3124 |
+
* @param $parameters
|
3125 |
+
*/
|
3126 |
+
function setGraphicsState($parameters)
|
3127 |
+
{
|
3128 |
+
// Create a new graphics state object if necessary
|
3129 |
+
if (($gstate = array_search($parameters, $this->gstates)) === false) {
|
3130 |
+
$this->numObj++;
|
3131 |
+
$this->o_extGState($this->numObj, 'new', $parameters);
|
3132 |
+
$gstate = $this->numStates;
|
3133 |
+
$this->gstates[$gstate] = $parameters;
|
3134 |
+
}
|
3135 |
+
$this->addContent("\n/GS$gstate gs");
|
3136 |
+
}
|
3137 |
+
|
3138 |
+
/**
|
3139 |
+
* Set current blend mode & opacity for lines.
|
3140 |
+
*
|
3141 |
+
* Valid blend modes are:
|
3142 |
+
*
|
3143 |
+
* Normal, Multiply, Screen, Overlay, Darken, Lighten,
|
3144 |
+
* ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
|
3145 |
+
* Exclusion
|
3146 |
+
*
|
3147 |
+
* @param string $mode the blend mode to use
|
3148 |
+
* @param float $opacity 0.0 fully transparent, 1.0 fully opaque
|
3149 |
+
*/
|
3150 |
+
function setLineTransparency($mode, $opacity)
|
3151 |
+
{
|
3152 |
+
static $blend_modes = array(
|
3153 |
+
"Normal",
|
3154 |
+
"Multiply",
|
3155 |
+
"Screen",
|
3156 |
+
"Overlay",
|
3157 |
+
"Darken",
|
3158 |
+
"Lighten",
|
3159 |
+
"ColorDogde",
|
3160 |
+
"ColorBurn",
|
3161 |
+
"HardLight",
|
3162 |
+
"SoftLight",
|
3163 |
+
"Difference",
|
3164 |
+
"Exclusion"
|
3165 |
+
);
|
3166 |
+
|
3167 |
+
if (!in_array($mode, $blend_modes)) {
|
3168 |
+
$mode = "Normal";
|
3169 |
+
}
|
3170 |
+
|
3171 |
+
// Only create a new graphics state if required
|
3172 |
+
if ($mode === $this->currentLineTransparency["mode"] &&
|
3173 |
+
$opacity == $this->currentLineTransparency["opacity"]
|
3174 |
+
) {
|
3175 |
+
return;
|
3176 |
+
}
|
3177 |
+
|
3178 |
+
$this->currentLineTransparency["mode"] = $mode;
|
3179 |
+
$this->currentLineTransparency["opacity"] = $opacity;
|
3180 |
+
|
3181 |
+
$options = array(
|
3182 |
+
"BM" => "/$mode",
|
3183 |
+
"CA" => (float)$opacity
|
3184 |
+
);
|
3185 |
+
|
3186 |
+
$this->setGraphicsState($options);
|
3187 |
+
}
|
3188 |
+
|
3189 |
+
/**
|
3190 |
+
* Set current blend mode & opacity for filled objects.
|
3191 |
+
*
|
3192 |
+
* Valid blend modes are:
|
3193 |
+
*
|
3194 |
+
* Normal, Multiply, Screen, Overlay, Darken, Lighten,
|
3195 |
+
* ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
|
3196 |
+
* Exclusion
|
3197 |
+
*
|
3198 |
+
* @param string $mode the blend mode to use
|
3199 |
+
* @param float $opacity 0.0 fully transparent, 1.0 fully opaque
|
3200 |
+
*/
|
3201 |
+
function setFillTransparency($mode, $opacity)
|
3202 |
+
{
|
3203 |
+
static $blend_modes = array(
|
3204 |
+
"Normal",
|
3205 |
+
"Multiply",
|
3206 |
+
"Screen",
|
3207 |
+
"Overlay",
|
3208 |
+
"Darken",
|
3209 |
+
"Lighten",
|
3210 |
+
"ColorDogde",
|
3211 |
+
"ColorBurn",
|
3212 |
+
"HardLight",
|
3213 |
+
"SoftLight",
|
3214 |
+
"Difference",
|
3215 |
+
"Exclusion"
|
3216 |
+
);
|
3217 |
+
|
3218 |
+
if (!in_array($mode, $blend_modes)) {
|
3219 |
+
$mode = "Normal";
|
3220 |
+
}
|
3221 |
+
|
3222 |
+
if ($mode === $this->currentFillTransparency["mode"] &&
|
3223 |
+
$opacity == $this->currentFillTransparency["opacity"]
|
3224 |
+
) {
|
3225 |
+
return;
|
3226 |
+
}
|
3227 |
+
|
3228 |
+
$this->currentFillTransparency["mode"] = $mode;
|
3229 |
+
$this->currentFillTransparency["opacity"] = $opacity;
|
3230 |
+
|
3231 |
+
$options = array(
|
3232 |
+
"BM" => "/$mode",
|
3233 |
+
"ca" => (float)$opacity,
|
3234 |
+
);
|
3235 |
+
|
3236 |
+
$this->setGraphicsState($options);
|
3237 |
+
}
|
3238 |
+
|
3239 |
+
/**
|
3240 |
+
* draw a line from one set of coordinates to another
|
3241 |
+
*
|
3242 |
+
* @param $x1
|
3243 |
+
* @param $y1
|
3244 |
+
* @param $x2
|
3245 |
+
* @param $y2
|
3246 |
+
* @param bool $stroke
|
3247 |
+
*/
|
3248 |
+
function line($x1, $y1, $x2, $y2, $stroke = true)
|
3249 |
+
{
|
3250 |
+
$this->addContent(sprintf("\n%.3F %.3F m %.3F %.3F l", $x1, $y1, $x2, $y2));
|
3251 |
+
|
3252 |
+
if ($stroke) {
|
3253 |
+
$this->addContent(' S');
|
3254 |
+
}
|
3255 |
+
}
|
3256 |
+
|
3257 |
+
/**
|
3258 |
+
* draw a bezier curve based on 4 control points
|
3259 |
+
*
|
3260 |
+
* @param $x0
|
3261 |
+
* @param $y0
|
3262 |
+
* @param $x1
|
3263 |
+
* @param $y1
|
3264 |
+
* @param $x2
|
3265 |
+
* @param $y2
|
3266 |
+
* @param $x3
|
3267 |
+
* @param $y3
|
3268 |
+
*/
|
3269 |
+
function curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
|
3270 |
+
{
|
3271 |
+
// in the current line style, draw a bezier curve from (x0,y0) to (x3,y3) using the other two points
|
3272 |
+
// as the control points for the curve.
|
3273 |
+
$this->addContent(
|
3274 |
+
sprintf("\n%.3F %.3F m %.3F %.3F %.3F %.3F %.3F %.3F c S", $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
|
3275 |
+
);
|
3276 |
+
}
|
3277 |
+
|
3278 |
+
/**
|
3279 |
+
* draw a part of an ellipse
|
3280 |
+
*
|
3281 |
+
* @param $x0
|
3282 |
+
* @param $y0
|
3283 |
+
* @param $astart
|
3284 |
+
* @param $afinish
|
3285 |
+
* @param $r1
|
3286 |
+
* @param int $r2
|
3287 |
+
* @param int $angle
|
3288 |
+
* @param int $nSeg
|
3289 |
+
*/
|
3290 |
+
function partEllipse($x0, $y0, $astart, $afinish, $r1, $r2 = 0, $angle = 0, $nSeg = 8)
|
3291 |
+
{
|
3292 |
+
$this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, false);
|
3293 |
+
}
|
3294 |
+
|
3295 |
+
/**
|
3296 |
+
* draw a filled ellipse
|
3297 |
+
*
|
3298 |
+
* @param $x0
|
3299 |
+
* @param $y0
|
3300 |
+
* @param $r1
|
3301 |
+
* @param int $r2
|
3302 |
+
* @param int $angle
|
3303 |
+
* @param int $nSeg
|
3304 |
+
* @param int $astart
|
3305 |
+
* @param int $afinish
|
3306 |
+
*/
|
3307 |
+
function filledEllipse($x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360)
|
3308 |
+
{
|
3309 |
+
$this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, true, true);
|
3310 |
+
}
|
3311 |
+
|
3312 |
+
/**
|
3313 |
+
* @param $x
|
3314 |
+
* @param $y
|
3315 |
+
*/
|
3316 |
+
function lineTo($x, $y)
|
3317 |
+
{
|
3318 |
+
$this->addContent(sprintf("\n%.3F %.3F l", $x, $y));
|
3319 |
+
}
|
3320 |
+
|
3321 |
+
/**
|
3322 |
+
* @param $x
|
3323 |
+
* @param $y
|
3324 |
+
*/
|
3325 |
+
function moveTo($x, $y)
|
3326 |
+
{
|
3327 |
+
$this->addContent(sprintf("\n%.3F %.3F m", $x, $y));
|
3328 |
+
}
|
3329 |
+
|
3330 |
+
/**
|
3331 |
+
* draw a bezier curve based on 4 control points
|
3332 |
+
*
|
3333 |
+
* @param $x1
|
3334 |
+
* @param $y1
|
3335 |
+
* @param $x2
|
3336 |
+
* @param $y2
|
3337 |
+
* @param $x3
|
3338 |
+
* @param $y3
|
3339 |
+
*/
|
3340 |
+
function curveTo($x1, $y1, $x2, $y2, $x3, $y3)
|
3341 |
+
{
|
3342 |
+
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F %.3F %.3F c", $x1, $y1, $x2, $y2, $x3, $y3));
|
3343 |
+
}
|
3344 |
+
|
3345 |
+
/**
|
3346 |
+
* draw a bezier curve based on 4 control points
|
3347 |
+
*/ function quadTo($cpx, $cpy, $x, $y)
|
3348 |
+
{
|
3349 |
+
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F v", $cpx, $cpy, $x, $y));
|
3350 |
+
}
|
3351 |
+
|
3352 |
+
function closePath()
|
3353 |
+
{
|
3354 |
+
$this->addContent(' h');
|
3355 |
+
}
|
3356 |
+
|
3357 |
+
function endPath()
|
3358 |
+
{
|
3359 |
+
$this->addContent(' n');
|
3360 |
+
}
|
3361 |
+
|
3362 |
+
/**
|
3363 |
+
* draw an ellipse
|
3364 |
+
* note that the part and filled ellipse are just special cases of this function
|
3365 |
+
*
|
3366 |
+
* draws an ellipse in the current line style
|
3367 |
+
* centered at $x0,$y0, radii $r1,$r2
|
3368 |
+
* if $r2 is not set, then a circle is drawn
|
3369 |
+
* from $astart to $afinish, measured in degrees, running anti-clockwise from the right hand side of the ellipse.
|
3370 |
+
* nSeg is not allowed to be less than 2, as this will simply draw a line (and will even draw a
|
3371 |
+
* pretty crappy shape at 2, as we are approximating with bezier curves.
|
3372 |
+
*
|
3373 |
+
* @param $x0
|
3374 |
+
* @param $y0
|
3375 |
+
* @param $r1
|
3376 |
+
* @param int $r2
|
3377 |
+
* @param int $angle
|
3378 |
+
* @param int $nSeg
|
3379 |
+
* @param int $astart
|
3380 |
+
* @param int $afinish
|
3381 |
+
* @param bool $close
|
3382 |
+
* @param bool $fill
|
3383 |
+
* @param bool $stroke
|
3384 |
+
* @param bool $incomplete
|
3385 |
+
*/
|
3386 |
+
function ellipse(
|
3387 |
+
$x0,
|
3388 |
+
$y0,
|
3389 |
+
$r1,
|
3390 |
+
$r2 = 0,
|
3391 |
+
$angle = 0,
|
3392 |
+
$nSeg = 8,
|
3393 |
+
$astart = 0,
|
3394 |
+
$afinish = 360,
|
3395 |
+
$close = true,
|
3396 |
+
$fill = false,
|
3397 |
+
$stroke = true,
|
3398 |
+
$incomplete = false
|
3399 |
+
) {
|
3400 |
+
if ($r1 == 0) {
|
3401 |
+
return;
|
3402 |
+
}
|
3403 |
+
|
3404 |
+
if ($r2 == 0) {
|
3405 |
+
$r2 = $r1;
|
3406 |
+
}
|
3407 |
+
|
3408 |
+
if ($nSeg < 2) {
|
3409 |
+
$nSeg = 2;
|
3410 |
+
}
|
3411 |
+
|
3412 |
+
$astart = deg2rad((float)$astart);
|
3413 |
+
$afinish = deg2rad((float)$afinish);
|
3414 |
+
$totalAngle = $afinish - $astart;
|
3415 |
+
|
3416 |
+
$dt = $totalAngle / $nSeg;
|
3417 |
+
$dtm = $dt / 3;
|
3418 |
+
|
3419 |
+
if ($angle != 0) {
|
3420 |
+
$a = -1 * deg2rad((float)$angle);
|
3421 |
+
|
3422 |
+
$this->addContent(
|
3423 |
+
sprintf("\n q %.3F %.3F %.3F %.3F %.3F %.3F cm", cos($a), -sin($a), sin($a), cos($a), $x0, $y0)
|
3424 |
+
);
|
3425 |
+
|
3426 |
+
$x0 = 0;
|
3427 |
+
$y0 = 0;
|
3428 |
+
}
|
3429 |
+
|
3430 |
+
$t1 = $astart;
|
3431 |
+
$a0 = $x0 + $r1 * cos($t1);
|
3432 |
+
$b0 = $y0 + $r2 * sin($t1);
|
3433 |
+
$c0 = -$r1 * sin($t1);
|
3434 |
+
$d0 = $r2 * cos($t1);
|
3435 |
+
|
3436 |
+
if (!$incomplete) {
|
3437 |
+
$this->addContent(sprintf("\n%.3F %.3F m ", $a0, $b0));
|
3438 |
+
}
|
3439 |
+
|
3440 |
+
for ($i = 1; $i <= $nSeg; $i++) {
|
3441 |
+
// draw this bit of the total curve
|
3442 |
+
$t1 = $i * $dt + $astart;
|
3443 |
+
$a1 = $x0 + $r1 * cos($t1);
|
3444 |
+
$b1 = $y0 + $r2 * sin($t1);
|
3445 |
+
$c1 = -$r1 * sin($t1);
|
3446 |
+
$d1 = $r2 * cos($t1);
|
3447 |
+
|
3448 |
+
$this->addContent(
|
3449 |
+
sprintf(
|
3450 |
+
"\n%.3F %.3F %.3F %.3F %.3F %.3F c",
|
3451 |
+
($a0 + $c0 * $dtm),
|
3452 |
+
($b0 + $d0 * $dtm),
|
3453 |
+
($a1 - $c1 * $dtm),
|
3454 |
+
($b1 - $d1 * $dtm),
|
3455 |
+
$a1,
|
3456 |
+
$b1
|
3457 |
+
)
|
3458 |
+
);
|
3459 |
+
|
3460 |
+
$a0 = $a1;
|
3461 |
+
$b0 = $b1;
|
3462 |
+
$c0 = $c1;
|
3463 |
+
$d0 = $d1;
|
3464 |
+
}
|
3465 |
+
|
3466 |
+
if (!$incomplete) {
|
3467 |
+
if ($fill) {
|
3468 |
+
$this->addContent(' f');
|
3469 |
+
}
|
3470 |
+
|
3471 |
+
if ($stroke) {
|
3472 |
+
if ($close) {
|
3473 |
+
$this->addContent(' s'); // small 's' signifies closing the path as well
|
3474 |
+
} else {
|
3475 |
+
$this->addContent(' S');
|
3476 |
+
}
|
3477 |
+
}
|
3478 |
+
}
|
3479 |
+
|
3480 |
+
if ($angle != 0) {
|
3481 |
+
$this->addContent(' Q');
|
3482 |
+
}
|
3483 |
+
}
|
3484 |
+
|
3485 |
+
/**
|
3486 |
+
* this sets the line drawing style.
|
3487 |
+
* width, is the thickness of the line in user units
|
3488 |
+
* cap is the type of cap to put on the line, values can be 'butt','round','square'
|
3489 |
+
* where the diffference between 'square' and 'butt' is that 'square' projects a flat end past the
|
3490 |
+
* end of the line.
|
3491 |
+
* join can be 'miter', 'round', 'bevel'
|
3492 |
+
* dash is an array which sets the dash pattern, is a series of length values, which are the lengths of the
|
3493 |
+
* on and off dashes.
|
3494 |
+
* (2) represents 2 on, 2 off, 2 on , 2 off ...
|
3495 |
+
* (2,1) is 2 on, 1 off, 2 on, 1 off.. etc
|
3496 |
+
* phase is a modifier on the dash pattern which is used to shift the point at which the pattern starts.
|
3497 |
+
*
|
3498 |
+
* @param int $width
|
3499 |
+
* @param string $cap
|
3500 |
+
* @param string $join
|
3501 |
+
* @param string $dash
|
3502 |
+
* @param int $phase
|
3503 |
+
*/
|
3504 |
+
function setLineStyle($width = 1, $cap = '', $join = '', $dash = '', $phase = 0)
|
3505 |
+
{
|
3506 |
+
// this is quite inefficient in that it sets all the parameters whenever 1 is changed, but will fix another day
|
3507 |
+
$string = '';
|
3508 |
+
|
3509 |
+
if ($width > 0) {
|
3510 |
+
$string .= "$width w";
|
3511 |
+
}
|
3512 |
+
|
3513 |
+
$ca = array('butt' => 0, 'round' => 1, 'square' => 2);
|
3514 |
+
|
3515 |
+
if (isset($ca[$cap])) {
|
3516 |
+
$string .= " $ca[$cap] J";
|
3517 |
+
}
|
3518 |
+
|
3519 |
+
$ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
|
3520 |
+
|
3521 |
+
if (isset($ja[$join])) {
|
3522 |
+
$string .= " $ja[$join] j";
|
3523 |
+
}
|
3524 |
+
|
3525 |
+
if (is_array($dash)) {
|
3526 |
+
$string .= ' [ ' . implode(' ', $dash) . " ] $phase d";
|
3527 |
+
}
|
3528 |
+
|
3529 |
+
$this->currentLineStyle = $string;
|
3530 |
+
$this->addContent("\n$string");
|
3531 |
+
}
|
3532 |
+
|
3533 |
+
/**
|
3534 |
+
* draw a polygon, the syntax for this is similar to the GD polygon command
|
3535 |
+
*
|
3536 |
+
* @param $p
|
3537 |
+
* @param $np
|
3538 |
+
* @param bool $f
|
3539 |
+
*/
|
3540 |
+
function polygon($p, $np, $f = false)
|
3541 |
+
{
|
3542 |
+
$this->addContent(sprintf("\n%.3F %.3F m ", $p[0], $p[1]));
|
3543 |
+
|
3544 |
+
for ($i = 2; $i < $np * 2; $i = $i + 2) {
|
3545 |
+
$this->addContent(sprintf("%.3F %.3F l ", $p[$i], $p[$i + 1]));
|
3546 |
+
}
|
3547 |
+
|
3548 |
+
if ($f) {
|
3549 |
+
$this->addContent(' f');
|
3550 |
+
} else {
|
3551 |
+
$this->addContent(' S');
|
3552 |
+
}
|
3553 |
+
}
|
3554 |
+
|
3555 |
+
/**
|
3556 |
+
* a filled rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
3557 |
+
* the coordinates of the upper-right corner
|
3558 |
+
*
|
3559 |
+
* @param $x1
|
3560 |
+
* @param $y1
|
3561 |
+
* @param $width
|
3562 |
+
* @param $height
|
3563 |
+
*/
|
3564 |
+
function filledRectangle($x1, $y1, $width, $height)
|
3565 |
+
{
|
3566 |
+
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re f", $x1, $y1, $width, $height));
|
3567 |
+
}
|
3568 |
+
|
3569 |
+
/**
|
3570 |
+
* draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
3571 |
+
* the coordinates of the upper-right corner
|
3572 |
+
*
|
3573 |
+
* @param $x1
|
3574 |
+
* @param $y1
|
3575 |
+
* @param $width
|
3576 |
+
* @param $height
|
3577 |
+
*/
|
3578 |
+
function rectangle($x1, $y1, $width, $height)
|
3579 |
+
{
|
3580 |
+
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re S", $x1, $y1, $width, $height));
|
3581 |
+
}
|
3582 |
+
|
3583 |
+
/**
|
3584 |
+
* draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
3585 |
+
* the coordinates of the upper-right corner
|
3586 |
+
*
|
3587 |
+
* @param $x1
|
3588 |
+
* @param $y1
|
3589 |
+
* @param $width
|
3590 |
+
* @param $height
|
3591 |
+
*/
|
3592 |
+
function rect($x1, $y1, $width, $height)
|
3593 |
+
{
|
3594 |
+
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re", $x1, $y1, $width, $height));
|
3595 |
+
}
|
3596 |
+
|
3597 |
+
function stroke()
|
3598 |
+
{
|
3599 |
+
$this->addContent("\nS");
|
3600 |
+
}
|
3601 |
+
|
3602 |
+
function fill()
|
3603 |
+
{
|
3604 |
+
$this->addContent("\nf" . ($this->fillRule === "evenodd" ? "*" : ""));
|
3605 |
+
}
|
3606 |
+
|
3607 |
+
function fillStroke()
|
3608 |
+
{
|
3609 |
+
$this->addContent("\nb" . ($this->fillRule === "evenodd" ? "*" : ""));
|
3610 |
+
}
|
3611 |
+
|
3612 |
+
/**
|
3613 |
+
* save the current graphic state
|
3614 |
+
*/
|
3615 |
+
function save()
|
3616 |
+
{
|
3617 |
+
// we must reset the color cache or it will keep bad colors after clipping
|
3618 |
+
$this->currentColor = null;
|
3619 |
+
$this->currentStrokeColor = null;
|
3620 |
+
$this->addContent("\nq");
|
3621 |
+
}
|
3622 |
+
|
3623 |
+
/**
|
3624 |
+
* restore the last graphic state
|
3625 |
+
*/
|
3626 |
+
function restore()
|
3627 |
+
{
|
3628 |
+
// we must reset the color cache or it will keep bad colors after clipping
|
3629 |
+
$this->currentColor = null;
|
3630 |
+
$this->currentStrokeColor = null;
|
3631 |
+
$this->addContent("\nQ");
|
3632 |
+
}
|
3633 |
+
|
3634 |
+
/**
|
3635 |
+
* draw a clipping rectangle, all the elements added after this will be clipped
|
3636 |
+
*
|
3637 |
+
* @param $x1
|
3638 |
+
* @param $y1
|
3639 |
+
* @param $width
|
3640 |
+
* @param $height
|
3641 |
+
*/
|
3642 |
+
function clippingRectangle($x1, $y1, $width, $height)
|
3643 |
+
{
|
3644 |
+
$this->save();
|
3645 |
+
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re W n", $x1, $y1, $width, $height));
|
3646 |
+
}
|
3647 |
+
|
3648 |
+
/**
|
3649 |
+
* draw a clipping rounded rectangle, all the elements added after this will be clipped
|
3650 |
+
*
|
3651 |
+
* @param $x1
|
3652 |
+
* @param $y1
|
3653 |
+
* @param $w
|
3654 |
+
* @param $h
|
3655 |
+
* @param $rTL
|
3656 |
+
* @param $rTR
|
3657 |
+
* @param $rBR
|
3658 |
+
* @param $rBL
|
3659 |
+
*/
|
3660 |
+
function clippingRectangleRounded($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
|
3661 |
+
{
|
3662 |
+
$this->save();
|
3663 |
+
|
3664 |
+
// start: top edge, left end
|
3665 |
+
$this->addContent(sprintf("\n%.3F %.3F m ", $x1, $y1 - $rTL + $h));
|
3666 |
+
|
3667 |
+
// line: bottom edge, left end
|
3668 |
+
$this->addContent(sprintf("\n%.3F %.3F l ", $x1, $y1 + $rBL));
|
3669 |
+
|
3670 |
+
// curve: bottom-left corner
|
3671 |
+
$this->ellipse($x1 + $rBL, $y1 + $rBL, $rBL, 0, 0, 8, 180, 270, false, false, false, true);
|
3672 |
+
|
3673 |
+
// line: right edge, bottom end
|
3674 |
+
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w - $rBR, $y1));
|
3675 |
+
|
3676 |
+
// curve: bottom-right corner
|
3677 |
+
$this->ellipse($x1 + $w - $rBR, $y1 + $rBR, $rBR, 0, 0, 8, 270, 360, false, false, false, true);
|
3678 |
+
|
3679 |
+
// line: right edge, top end
|
3680 |
+
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w, $y1 + $h - $rTR));
|
3681 |
+
|
3682 |
+
// curve: bottom-right corner
|
3683 |
+
$this->ellipse($x1 + $w - $rTR, $y1 + $h - $rTR, $rTR, 0, 0, 8, 0, 90, false, false, false, true);
|
3684 |
+
|
3685 |
+
// line: bottom edge, right end
|
3686 |
+
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rTL, $y1 + $h));
|
3687 |
+
|
3688 |
+
// curve: top-right corner
|
3689 |
+
$this->ellipse($x1 + $rTL, $y1 + $h - $rTL, $rTL, 0, 0, 8, 90, 180, false, false, false, true);
|
3690 |
+
|
3691 |
+
// line: top edge, left end
|
3692 |
+
$this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rBL, $y1));
|
3693 |
+
|
3694 |
+
// Close & clip
|
3695 |
+
$this->addContent(" W n");
|
3696 |
+
}
|
3697 |
+
|
3698 |
+
/**
|
3699 |
+
* ends the last clipping shape
|
3700 |
+
*/
|
3701 |
+
function clippingEnd()
|
3702 |
+
{
|
3703 |
+
$this->restore();
|
3704 |
+
}
|
3705 |
+
|
3706 |
+
/**
|
3707 |
+
* scale
|
3708 |
+
*
|
3709 |
+
* @param float $s_x scaling factor for width as percent
|
3710 |
+
* @param float $s_y scaling factor for height as percent
|
3711 |
+
* @param float $x Origin abscissa
|
3712 |
+
* @param float $y Origin ordinate
|
3713 |
+
*/
|
3714 |
+
function scale($s_x, $s_y, $x, $y)
|
3715 |
+
{
|
3716 |
+
$y = $this->currentPageSize["height"] - $y;
|
3717 |
+
|
3718 |
+
$tm = array(
|
3719 |
+
$s_x,
|
3720 |
+
0,
|
3721 |
+
0,
|
3722 |
+
$s_y,
|
3723 |
+
$x * (1 - $s_x),
|
3724 |
+
$y * (1 - $s_y)
|
3725 |
+
);
|
3726 |
+
|
3727 |
+
$this->transform($tm);
|
3728 |
+
}
|
3729 |
+
|
3730 |
+
/**
|
3731 |
+
* translate
|
3732 |
+
*
|
3733 |
+
* @param float $t_x movement to the right
|
3734 |
+
* @param float $t_y movement to the bottom
|
3735 |
+
*/
|
3736 |
+
function translate($t_x, $t_y)
|
3737 |
+
{
|
3738 |
+
$tm = array(
|
3739 |
+
1,
|
3740 |
+
0,
|
3741 |
+
0,
|
3742 |
+
1,
|
3743 |
+
$t_x,
|
3744 |
+
-$t_y
|
3745 |
+
);
|
3746 |
+
|
3747 |
+
$this->transform($tm);
|
3748 |
+
}
|
3749 |
+
|
3750 |
+
/**
|
3751 |
+
* rotate
|
3752 |
+
*
|
3753 |
+
* @param float $angle angle in degrees for counter-clockwise rotation
|
3754 |
+
* @param float $x Origin abscissa
|
3755 |
+
* @param float $y Origin ordinate
|
3756 |
+
*/
|
3757 |
+
function rotate($angle, $x, $y)
|
3758 |
+
{
|
3759 |
+
$y = $this->currentPageSize["height"] - $y;
|
3760 |
+
|
3761 |
+
$a = deg2rad($angle);
|
3762 |
+
$cos_a = cos($a);
|
3763 |
+
$sin_a = sin($a);
|
3764 |
+
|
3765 |
+
$tm = array(
|
3766 |
+
$cos_a,
|
3767 |
+
-$sin_a,
|
3768 |
+
$sin_a,
|
3769 |
+
$cos_a,
|
3770 |
+
$x - $sin_a * $y - $cos_a * $x,
|
3771 |
+
$y - $cos_a * $y + $sin_a * $x,
|
3772 |
+
);
|
3773 |
+
|
3774 |
+
$this->transform($tm);
|
3775 |
+
}
|
3776 |
+
|
3777 |
+
/**
|
3778 |
+
* skew
|
3779 |
+
*
|
3780 |
+
* @param float $angle_x
|
3781 |
+
* @param float $angle_y
|
3782 |
+
* @param float $x Origin abscissa
|
3783 |
+
* @param float $y Origin ordinate
|
3784 |
+
*/
|
3785 |
+
function skew($angle_x, $angle_y, $x, $y)
|
3786 |
+
{
|
3787 |
+
$y = $this->currentPageSize["height"] - $y;
|
3788 |
+
|
3789 |
+
$tan_x = tan(deg2rad($angle_x));
|
3790 |
+
$tan_y = tan(deg2rad($angle_y));
|
3791 |
+
|
3792 |
+
$tm = array(
|
3793 |
+
1,
|
3794 |
+
-$tan_y,
|
3795 |
+
-$tan_x,
|
3796 |
+
1,
|
3797 |
+
$tan_x * $y,
|
3798 |
+
$tan_y * $x,
|
3799 |
+
);
|
3800 |
+
|
3801 |
+
$this->transform($tm);
|
3802 |
+
}
|
3803 |
+
|
3804 |
+
/**
|
3805 |
+
* apply graphic transformations
|
3806 |
+
*
|
3807 |
+
* @param array $tm transformation matrix
|
3808 |
+
*/
|
3809 |
+
function transform($tm)
|
3810 |
+
{
|
3811 |
+
$this->addContent(vsprintf("\n %.3F %.3F %.3F %.3F %.3F %.3F cm", $tm));
|
3812 |
+
}
|
3813 |
+
|
3814 |
+
/**
|
3815 |
+
* add a new page to the document
|
3816 |
+
* this also makes the new page the current active object
|
3817 |
+
*
|
3818 |
+
* @param int $insert
|
3819 |
+
* @param int $id
|
3820 |
+
* @param string $pos
|
3821 |
+
* @return int
|
3822 |
+
*/
|
3823 |
+
function newPage($insert = 0, $id = 0, $pos = 'after')
|
3824 |
+
{
|
3825 |
+
// if there is a state saved, then go up the stack closing them
|
3826 |
+
// then on the new page, re-open them with the right setings
|
3827 |
+
|
3828 |
+
if ($this->nStateStack) {
|
3829 |
+
for ($i = $this->nStateStack; $i >= 1; $i--) {
|
3830 |
+
$this->restoreState($i);
|
3831 |
+
}
|
3832 |
+
}
|
3833 |
+
|
3834 |
+
$this->numObj++;
|
3835 |
+
|
3836 |
+
if ($insert) {
|
3837 |
+
// the id from the ezPdf class is the id of the contents of the page, not the page object itself
|
3838 |
+
// query that object to find the parent
|
3839 |
+
$rid = $this->objects[$id]['onPage'];
|
3840 |
+
$opt = array('rid' => $rid, 'pos' => $pos);
|
3841 |
+
$this->o_page($this->numObj, 'new', $opt);
|
3842 |
+
} else {
|
3843 |
+
$this->o_page($this->numObj, 'new');
|
3844 |
+
}
|
3845 |
+
|
3846 |
+
// if there is a stack saved, then put that onto the page
|
3847 |
+
if ($this->nStateStack) {
|
3848 |
+
for ($i = 1; $i <= $this->nStateStack; $i++) {
|
3849 |
+
$this->saveState($i);
|
3850 |
+
}
|
3851 |
+
}
|
3852 |
+
|
3853 |
+
// and if there has been a stroke or fill color set, then transfer them
|
3854 |
+
if (isset($this->currentColor)) {
|
3855 |
+
$this->setColor($this->currentColor, true);
|
3856 |
+
}
|
3857 |
+
|
3858 |
+
if (isset($this->currentStrokeColor)) {
|
3859 |
+
$this->setStrokeColor($this->currentStrokeColor, true);
|
3860 |
+
}
|
3861 |
+
|
3862 |
+
// if there is a line style set, then put this in too
|
3863 |
+
if (mb_strlen($this->currentLineStyle, '8bit')) {
|
3864 |
+
$this->addContent("\n$this->currentLineStyle");
|
3865 |
+
}
|
3866 |
+
|
3867 |
+
// the call to the o_page object set currentContents to the present page, so this can be returned as the page id
|
3868 |
+
return $this->currentContents;
|
3869 |
+
}
|
3870 |
+
|
3871 |
+
/**
|
3872 |
+
* Streams the PDF to the client.
|
3873 |
+
*
|
3874 |
+
* @param string $filename The filename to present to the client.
|
3875 |
+
* @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1).
|
3876 |
+
*/
|
3877 |
+
function stream($filename = "document.pdf", $options = array())
|
3878 |
+
{
|
3879 |
+
if (headers_sent()) {
|
3880 |
+
die("Unable to stream pdf: headers already sent");
|
3881 |
+
}
|
3882 |
+
|
3883 |
+
if (!isset($options["compress"])) $options["compress"] = true;
|
3884 |
+
if (!isset($options["Attachment"])) $options["Attachment"] = true;
|
3885 |
+
|
3886 |
+
$debug = !$options['compress'];
|
3887 |
+
$tmp = ltrim($this->output($debug));
|
3888 |
+
|
3889 |
+
header("Cache-Control: private");
|
3890 |
+
header("Content-Type: application/pdf");
|
3891 |
+
header("Content-Length: " . mb_strlen($tmp, "8bit"));
|
3892 |
+
|
3893 |
+
$filename = str_replace(array("\n", "'"), "", basename($filename, ".pdf")) . ".pdf";
|
3894 |
+
$attachment = $options["Attachment"] ? "attachment" : "inline";
|
3895 |
+
|
3896 |
+
$encoding = mb_detect_encoding($filename);
|
3897 |
+
$fallbackfilename = mb_convert_encoding($filename, "ISO-8859-1", $encoding);
|
3898 |
+
$fallbackfilename = str_replace("\"", "", $fallbackfilename);
|
3899 |
+
$encodedfilename = rawurlencode($filename);
|
3900 |
+
|
3901 |
+
$contentDisposition = "Content-Disposition: $attachment; filename=\"$fallbackfilename\"";
|
3902 |
+
if ($fallbackfilename !== $filename) {
|
3903 |
+
$contentDisposition .= "; filename*=UTF-8''$encodedfilename";
|
3904 |
+
}
|
3905 |
+
header($contentDisposition);
|
3906 |
+
|
3907 |
+
echo $tmp;
|
3908 |
+
flush();
|
3909 |
+
}
|
3910 |
+
|
3911 |
+
/**
|
3912 |
+
* return the height in units of the current font in the given size
|
3913 |
+
*
|
3914 |
+
* @param $size
|
3915 |
+
* @return float|int
|
3916 |
+
*/
|
3917 |
+
function getFontHeight($size)
|
3918 |
+
{
|
3919 |
+
if (!$this->numFonts) {
|
3920 |
+
$this->selectFont($this->defaultFont);
|
3921 |
+
}
|
3922 |
+
|
3923 |
+
$font = $this->fonts[$this->currentFont];
|
3924 |
+
|
3925 |
+
// for the current font, and the given size, what is the height of the font in user units
|
3926 |
+
if (isset($font['Ascender']) && isset($font['Descender'])) {
|
3927 |
+
$h = $font['Ascender'] - $font['Descender'];
|
3928 |
+
} else {
|
3929 |
+
$h = $font['FontBBox'][3] - $font['FontBBox'][1];
|
3930 |
+
}
|
3931 |
+
|
3932 |
+
// have to adjust by a font offset for Windows fonts. unfortunately it looks like
|
3933 |
+
// the bounding box calculations are wrong and I don't know why.
|
3934 |
+
if (isset($font['FontHeightOffset'])) {
|
3935 |
+
// For CourierNew from Windows this needs to be -646 to match the
|
3936 |
+
// Adobe native Courier font.
|
3937 |
+
//
|
3938 |
+
// For FreeMono from GNU this needs to be -337 to match the
|
3939 |
+
// Courier font.
|
3940 |
+
//
|
3941 |
+
// Both have been added manually to the .afm and .ufm files.
|
3942 |
+
$h += (int)$font['FontHeightOffset'];
|
3943 |
+
}
|
3944 |
+
|
3945 |
+
return $size * $h / 1000;
|
3946 |
+
}
|
3947 |
+
|
3948 |
+
/**
|
3949 |
+
* @param $size
|
3950 |
+
* @return float|int
|
3951 |
+
*/
|
3952 |
+
function getFontXHeight($size)
|
3953 |
+
{
|
3954 |
+
if (!$this->numFonts) {
|
3955 |
+
$this->selectFont($this->defaultFont);
|
3956 |
+
}
|
3957 |
+
|
3958 |
+
$font = $this->fonts[$this->currentFont];
|
3959 |
+
|
3960 |
+
// for the current font, and the given size, what is the height of the font in user units
|
3961 |
+
if (isset($font['XHeight'])) {
|
3962 |
+
$xh = $font['Ascender'] - $font['Descender'];
|
3963 |
+
} else {
|
3964 |
+
$xh = $this->getFontHeight($size) / 2;
|
3965 |
+
}
|
3966 |
+
|
3967 |
+
return $size * $xh / 1000;
|
3968 |
+
}
|
3969 |
+
|
3970 |
+
/**
|
3971 |
+
* return the font descender, this will normally return a negative number
|
3972 |
+
* if you add this number to the baseline, you get the level of the bottom of the font
|
3973 |
+
* it is in the pdf user units
|
3974 |
+
*
|
3975 |
+
* @param $size
|
3976 |
+
* @return float|int
|
3977 |
+
*/
|
3978 |
+
function getFontDescender($size)
|
3979 |
+
{
|
3980 |
+
// note that this will most likely return a negative value
|
3981 |
+
if (!$this->numFonts) {
|
3982 |
+
$this->selectFont($this->defaultFont);
|
3983 |
+
}
|
3984 |
+
|
3985 |
+
//$h = $this->fonts[$this->currentFont]['FontBBox'][1];
|
3986 |
+
$h = $this->fonts[$this->currentFont]['Descender'];
|
3987 |
+
|
3988 |
+
return $size * $h / 1000;
|
3989 |
+
}
|
3990 |
+
|
3991 |
+
/**
|
3992 |
+
* filter the text, this is applied to all text just before being inserted into the pdf document
|
3993 |
+
* it escapes the various things that need to be escaped, and so on
|
3994 |
+
*
|
3995 |
+
* @access private
|
3996 |
+
*
|
3997 |
+
* @param $text
|
3998 |
+
* @param bool $bom
|
3999 |
+
* @param bool $convert_encoding
|
4000 |
+
* @return string
|
4001 |
+
*/
|
4002 |
+
function filterText($text, $bom = true, $convert_encoding = true)
|
4003 |
+
{
|
4004 |
+
if (!$this->numFonts) {
|
4005 |
+
$this->selectFont($this->defaultFont);
|
4006 |
+
}
|
4007 |
+
|
4008 |
+
if ($convert_encoding) {
|
4009 |
+
$cf = $this->currentFont;
|
4010 |
+
if (isset($this->fonts[$cf]) && $this->fonts[$cf]['isUnicode']) {
|
4011 |
+
$text = $this->utf8toUtf16BE($text, $bom);
|
4012 |
+
} else {
|
4013 |
+
//$text = html_entity_decode($text, ENT_QUOTES);
|
4014 |
+
$text = mb_convert_encoding($text, self::$targetEncoding, 'UTF-8');
|
4015 |
+
}
|
4016 |
+
} else if ($bom) {
|
4017 |
+
$text = $this->utf8toUtf16BE($text, $bom);
|
4018 |
+
}
|
4019 |
+
|
4020 |
+
// the chr(13) substitution fixes a bug seen in TCPDF (bug #1421290)
|
4021 |
+
return strtr($text, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
|
4022 |
+
}
|
4023 |
+
|
4024 |
+
/**
|
4025 |
+
* return array containing codepoints (UTF-8 character values) for the
|
4026 |
+
* string passed in.
|
4027 |
+
*
|
4028 |
+
* based on the excellent TCPDF code by Nicola Asuni and the
|
4029 |
+
* RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
|
4030 |
+
*
|
4031 |
+
* @access private
|
4032 |
+
* @author Orion Richardson
|
4033 |
+
* @since January 5, 2008
|
4034 |
+
*
|
4035 |
+
* @param string $text UTF-8 string to process
|
4036 |
+
*
|
4037 |
+
* @return array UTF-8 codepoints array for the string
|
4038 |
+
*/
|
4039 |
+
function utf8toCodePointsArray(&$text)
|
4040 |
+
{
|
4041 |
+
$length = mb_strlen($text, '8bit'); // http://www.php.net/manual/en/function.mb-strlen.php#77040
|
4042 |
+
$unicode = array(); // array containing unicode values
|
4043 |
+
$bytes = array(); // array containing single character byte sequences
|
4044 |
+
$numbytes = 1; // number of octets needed to represent the UTF-8 character
|
4045 |
+
|
4046 |
+
for ($i = 0; $i < $length; $i++) {
|
4047 |
+
$c = ord($text[$i]); // get one string character at time
|
4048 |
+
if (count($bytes) === 0) { // get starting octect
|
4049 |
+
if ($c <= 0x7F) {
|
4050 |
+
$unicode[] = $c; // use the character "as is" because is ASCII
|
4051 |
+
$numbytes = 1;
|
4052 |
+
} elseif (($c >> 0x05) === 0x06) { // 2 bytes character (0x06 = 110 BIN)
|
4053 |
+
$bytes[] = ($c - 0xC0) << 0x06;
|
4054 |
+
$numbytes = 2;
|
4055 |
+
} elseif (($c >> 0x04) === 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
|
4056 |
+
$bytes[] = ($c - 0xE0) << 0x0C;
|
4057 |
+
$numbytes = 3;
|
4058 |
+
} elseif (($c >> 0x03) === 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
|
4059 |
+
$bytes[] = ($c - 0xF0) << 0x12;
|
4060 |
+
$numbytes = 4;
|
4061 |
+
} else {
|
4062 |
+
// use replacement character for other invalid sequences
|
4063 |
+
$unicode[] = 0xFFFD;
|
4064 |
+
$bytes = array();
|
4065 |
+
$numbytes = 1;
|
4066 |
+
}
|
4067 |
+
} elseif (($c >> 0x06) === 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
|
4068 |
+
$bytes[] = $c - 0x80;
|
4069 |
+
if (count($bytes) === $numbytes) {
|
4070 |
+
// compose UTF-8 bytes to a single unicode value
|
4071 |
+
$c = $bytes[0];
|
4072 |
+
for ($j = 1; $j < $numbytes; $j++) {
|
4073 |
+
$c += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
|
4074 |
+
}
|
4075 |
+
if ((($c >= 0xD800) AND ($c <= 0xDFFF)) OR ($c >= 0x10FFFF)) {
|
4076 |
+
// The definition of UTF-8 prohibits encoding character numbers between
|
4077 |
+
// U+D800 and U+DFFF, which are reserved for use with the UTF-16
|
4078 |
+
// encoding form (as surrogate pairs) and do not directly represent
|
4079 |
+
// characters.
|
4080 |
+
$unicode[] = 0xFFFD; // use replacement character
|
4081 |
+
} else {
|
4082 |
+
$unicode[] = $c; // add char to array
|
4083 |
+
}
|
4084 |
+
// reset data for next char
|
4085 |
+
$bytes = array();
|
4086 |
+
$numbytes = 1;
|
4087 |
+
}
|
4088 |
+
} else {
|
4089 |
+
// use replacement character for other invalid sequences
|
4090 |
+
$unicode[] = 0xFFFD;
|
4091 |
+
$bytes = array();
|
4092 |
+
$numbytes = 1;
|
4093 |
+
}
|
4094 |
+
}
|
4095 |
+
|
4096 |
+
return $unicode;
|
4097 |
+
}
|
4098 |
+
|
4099 |
+
/**
|
4100 |
+
* convert UTF-8 to UTF-16 with an additional byte order marker
|
4101 |
+
* at the front if required.
|
4102 |
+
*
|
4103 |
+
* based on the excellent TCPDF code by Nicola Asuni and the
|
4104 |
+
* RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
|
4105 |
+
*
|
4106 |
+
* @access private
|
4107 |
+
* @author Orion Richardson
|
4108 |
+
* @since January 5, 2008
|
4109 |
+
*
|
4110 |
+
* @param string $text UTF-8 string to process
|
4111 |
+
* @param boolean $bom whether to add the byte order marker
|
4112 |
+
*
|
4113 |
+
* @return string UTF-16 result string
|
4114 |
+
*/
|
4115 |
+
function utf8toUtf16BE(&$text, $bom = true)
|
4116 |
+
{
|
4117 |
+
$out = $bom ? "\xFE\xFF" : '';
|
4118 |
+
|
4119 |
+
$unicode = $this->utf8toCodePointsArray($text);
|
4120 |
+
foreach ($unicode as $c) {
|
4121 |
+
if ($c === 0xFFFD) {
|
4122 |
+
$out .= "\xFF\xFD"; // replacement character
|
4123 |
+
} elseif ($c < 0x10000) {
|
4124 |
+
$out .= chr($c >> 0x08) . chr($c & 0xFF);
|
4125 |
+
} else {
|
4126 |
+
$c -= 0x10000;
|
4127 |
+
$w1 = 0xD800 | ($c >> 0x10);
|
4128 |
+
$w2 = 0xDC00 | ($c & 0x3FF);
|
4129 |
+
$out .= chr($w1 >> 0x08) . chr($w1 & 0xFF) . chr($w2 >> 0x08) . chr($w2 & 0xFF);
|
4130 |
+
}
|
4131 |
+
}
|
4132 |
+
|
4133 |
+
return $out;
|
4134 |
+
}
|
4135 |
+
|
4136 |
+
/**
|
4137 |
+
* given a start position and information about how text is to be laid out, calculate where
|
4138 |
+
* on the page the text will end
|
4139 |
+
*
|
4140 |
+
* @param $x
|
4141 |
+
* @param $y
|
4142 |
+
* @param $angle
|
4143 |
+
* @param $size
|
4144 |
+
* @param $wa
|
4145 |
+
* @param $text
|
4146 |
+
* @return array
|
4147 |
+
*/
|
4148 |
+
private function getTextPosition($x, $y, $angle, $size, $wa, $text)
|
4149 |
+
{
|
4150 |
+
// given this information return an array containing x and y for the end position as elements 0 and 1
|
4151 |
+
$w = $this->getTextWidth($size, $text);
|
4152 |
+
|
4153 |
+
// need to adjust for the number of spaces in this text
|
4154 |
+
$words = explode(' ', $text);
|
4155 |
+
$nspaces = count($words) - 1;
|
4156 |
+
$w += $wa * $nspaces;
|
4157 |
+
$a = deg2rad((float)$angle);
|
4158 |
+
|
4159 |
+
return array(cos($a) * $w + $x, -sin($a) * $w + $y);
|
4160 |
+
}
|
4161 |
+
|
4162 |
+
/**
|
4163 |
+
* Callback method used by smallCaps
|
4164 |
+
*
|
4165 |
+
* @param array $matches
|
4166 |
+
*
|
4167 |
+
* @return string
|
4168 |
+
*/
|
4169 |
+
function toUpper($matches)
|
4170 |
+
{
|
4171 |
+
return mb_strtoupper($matches[0]);
|
4172 |
+
}
|
4173 |
+
|
4174 |
+
function concatMatches($matches)
|
4175 |
+
{
|
4176 |
+
$str = "";
|
4177 |
+
foreach ($matches as $match) {
|
4178 |
+
$str .= $match[0];
|
4179 |
+
}
|
4180 |
+
|
4181 |
+
return $str;
|
4182 |
+
}
|
4183 |
+
|
4184 |
+
/**
|
4185 |
+
* register text for font subsetting
|
4186 |
+
*
|
4187 |
+
* @param $font
|
4188 |
+
* @param $text
|
4189 |
+
*/
|
4190 |
+
function registerText($font, $text)
|
4191 |
+
{
|
4192 |
+
if (!$this->isUnicode || in_array(mb_strtolower(basename($font)), self::$coreFonts)) {
|
4193 |
+
return;
|
4194 |
+
}
|
4195 |
+
|
4196 |
+
if (!isset($this->stringSubsets[$font])) {
|
4197 |
+
$this->stringSubsets[$font] = array();
|
4198 |
+
}
|
4199 |
+
|
4200 |
+
$this->stringSubsets[$font] = array_unique(
|
4201 |
+
array_merge($this->stringSubsets[$font], $this->utf8toCodePointsArray($text))
|
4202 |
+
);
|
4203 |
+
}
|
4204 |
+
|
4205 |
+
/**
|
4206 |
+
* add text to the document, at a specified location, size and angle on the page
|
4207 |
+
*
|
4208 |
+
* @param $x
|
4209 |
+
* @param $y
|
4210 |
+
* @param $size
|
4211 |
+
* @param $text
|
4212 |
+
* @param int $angle
|
4213 |
+
* @param int $wordSpaceAdjust
|
4214 |
+
* @param int $charSpaceAdjust
|
4215 |
+
* @param bool $smallCaps
|
4216 |
+
*/
|
4217 |
+
function addText($x, $y, $size, $text, $angle = 0, $wordSpaceAdjust = 0, $charSpaceAdjust = 0, $smallCaps = false)
|
4218 |
+
{
|
4219 |
+
if (!$this->numFonts) {
|
4220 |
+
$this->selectFont($this->defaultFont);
|
4221 |
+
}
|
4222 |
+
|
4223 |
+
$text = str_replace(array("\r", "\n"), "", $text);
|
4224 |
+
|
4225 |
+
if ($smallCaps) {
|
4226 |
+
preg_match_all("/(\P{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
|
4227 |
+
$lower = $this->concatMatches($matches);
|
4228 |
+
d($lower);
|
4229 |
+
|
4230 |
+
preg_match_all("/(\p{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
|
4231 |
+
$other = $this->concatMatches($matches);
|
4232 |
+
d($other);
|
4233 |
+
|
4234 |
+
//$text = preg_replace_callback("/\p{Ll}/u", array($this, "toUpper"), $text);
|
4235 |
+
}
|
4236 |
+
|
4237 |
+
// if there are any open callbacks, then they should be called, to show the start of the line
|
4238 |
+
if ($this->nCallback > 0) {
|
4239 |
+
for ($i = $this->nCallback; $i > 0; $i--) {
|
4240 |
+
// call each function
|
4241 |
+
$info = array(
|
4242 |
+
'x' => $x,
|
4243 |
+
'y' => $y,
|
4244 |
+
'angle' => $angle,
|
4245 |
+
'status' => 'sol',
|
4246 |
+
'p' => $this->callback[$i]['p'],
|
4247 |
+
'nCallback' => $this->callback[$i]['nCallback'],
|
4248 |
+
'height' => $this->callback[$i]['height'],
|
4249 |
+
'descender' => $this->callback[$i]['descender']
|
4250 |
+
);
|
4251 |
+
|
4252 |
+
$func = $this->callback[$i]['f'];
|
4253 |
+
$this->$func($info);
|
4254 |
+
}
|
4255 |
+
}
|
4256 |
+
|
4257 |
+
if ($angle == 0) {
|
4258 |
+
$this->addContent(sprintf("\nBT %.3F %.3F Td", $x, $y));
|
4259 |
+
} else {
|
4260 |
+
$a = deg2rad((float)$angle);
|
4261 |
+
$this->addContent(
|
4262 |
+
sprintf("\nBT %.3F %.3F %.3F %.3F %.3F %.3F Tm", cos($a), -sin($a), sin($a), cos($a), $x, $y)
|
4263 |
+
);
|
4264 |
+
}
|
4265 |
+
|
4266 |
+
if ($wordSpaceAdjust != 0) {
|
4267 |
+
$this->addContent(sprintf(" %.3F Tw", $wordSpaceAdjust));
|
4268 |
+
}
|
4269 |
+
|
4270 |
+
if ($charSpaceAdjust != 0) {
|
4271 |
+
$this->addContent(sprintf(" %.3F Tc", $charSpaceAdjust));
|
4272 |
+
}
|
4273 |
+
|
4274 |
+
$len = mb_strlen($text);
|
4275 |
+
$start = 0;
|
4276 |
+
|
4277 |
+
if ($start < $len) {
|
4278 |
+
$part = $text; // OAR - Don't need this anymore, given that $start always equals zero. substr($text, $start);
|
4279 |
+
$place_text = $this->filterText($part, false);
|
4280 |
+
// modify unicode text so that extra word spacing is manually implemented (bug #)
|
4281 |
+
$cf = $this->currentFont;
|
4282 |
+
if ($this->fonts[$cf]['isUnicode'] && $wordSpaceAdjust != 0) {
|
4283 |
+
$space_scale = 1000 / $size;
|
4284 |
+
$place_text = str_replace("\x00\x20", "\x00\x20)\x00\x20" . (-round($space_scale * $wordSpaceAdjust)) . "\x00\x20(", $place_text);
|
4285 |
+
}
|
4286 |
+
$this->addContent(" /F$this->currentFontNum " . sprintf('%.1F Tf ', $size));
|
4287 |
+
$this->addContent(" [($place_text)] TJ");
|
4288 |
+
}
|
4289 |
+
|
4290 |
+
if ($wordSpaceAdjust != 0) {
|
4291 |
+
$this->addContent(sprintf(" %.3F Tw", 0));
|
4292 |
+
}
|
4293 |
+
|
4294 |
+
if ($charSpaceAdjust != 0) {
|
4295 |
+
$this->addContent(sprintf(" %.3F Tc", 0));
|
4296 |
+
}
|
4297 |
+
|
4298 |
+
$this->addContent(' ET');
|
4299 |
+
|
4300 |
+
// if there are any open callbacks, then they should be called, to show the end of the line
|
4301 |
+
if ($this->nCallback > 0) {
|
4302 |
+
for ($i = $this->nCallback; $i > 0; $i--) {
|
4303 |
+
// call each function
|
4304 |
+
$tmp = $this->getTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, $text);
|
4305 |
+
$info = array(
|
4306 |
+
'x' => $tmp[0],
|
4307 |
+
'y' => $tmp[1],
|
4308 |
+
'angle' => $angle,
|
4309 |
+
'status' => 'eol',
|
4310 |
+
'p' => $this->callback[$i]['p'],
|
4311 |
+
'nCallback' => $this->callback[$i]['nCallback'],
|
4312 |
+
'height' => $this->callback[$i]['height'],
|
4313 |
+
'descender' => $this->callback[$i]['descender']
|
4314 |
+
);
|
4315 |
+
$func = $this->callback[$i]['f'];
|
4316 |
+
$this->$func($info);
|
4317 |
+
}
|
4318 |
+
}
|
4319 |
+
}
|
4320 |
+
|
4321 |
+
/**
|
4322 |
+
* calculate how wide a given text string will be on a page, at a given size.
|
4323 |
+
* this can be called externally, but is also used by the other class functions
|
4324 |
+
*
|
4325 |
+
* @param $size
|
4326 |
+
* @param $text
|
4327 |
+
* @param int $word_spacing
|
4328 |
+
* @param int $char_spacing
|
4329 |
+
* @return float|int
|
4330 |
+
*/
|
4331 |
+
function getTextWidth($size, $text, $word_spacing = 0, $char_spacing = 0)
|
4332 |
+
{
|
4333 |
+
static $ord_cache = array();
|
4334 |
+
|
4335 |
+
// this function should not change any of the settings, though it will need to
|
4336 |
+
// track any directives which change during calculation, so copy them at the start
|
4337 |
+
// and put them back at the end.
|
4338 |
+
$store_currentTextState = $this->currentTextState;
|
4339 |
+
|
4340 |
+
if (!$this->numFonts) {
|
4341 |
+
$this->selectFont($this->defaultFont);
|
4342 |
+
}
|
4343 |
+
|
4344 |
+
$text = str_replace(array("\r", "\n"), "", $text);
|
4345 |
+
|
4346 |
+
// converts a number or a float to a string so it can get the width
|
4347 |
+
$text = "$text";
|
4348 |
+
|
4349 |
+
// hmm, this is where it all starts to get tricky - use the font information to
|
4350 |
+
// calculate the width of each character, add them up and convert to user units
|
4351 |
+
$w = 0;
|
4352 |
+
$cf = $this->currentFont;
|
4353 |
+
$current_font = $this->fonts[$cf];
|
4354 |
+
$space_scale = 1000 / ($size > 0 ? $size : 1);
|
4355 |
+
$n_spaces = 0;
|
4356 |
+
|
4357 |
+
if ($current_font['isUnicode']) {
|
4358 |
+
// for Unicode, use the code points array to calculate width rather
|
4359 |
+
// than just the string itself
|
4360 |
+
$unicode = $this->utf8toCodePointsArray($text);
|
4361 |
+
|
4362 |
+
foreach ($unicode as $char) {
|
4363 |
+
// check if we have to replace character
|
4364 |
+
if (isset($current_font['differences'][$char])) {
|
4365 |
+
$char = $current_font['differences'][$char];
|
4366 |
+
}
|
4367 |
+
|
4368 |
+
if (isset($current_font['C'][$char])) {
|
4369 |
+
$char_width = $current_font['C'][$char];
|
4370 |
+
|
4371 |
+
// add the character width
|
4372 |
+
$w += $char_width;
|
4373 |
+
|
4374 |
+
// add additional padding for space
|
4375 |
+
if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space
|
4376 |
+
$w += $word_spacing * $space_scale;
|
4377 |
+
$n_spaces++;
|
4378 |
+
}
|
4379 |
+
}
|
4380 |
+
}
|
4381 |
+
|
4382 |
+
// add additional char spacing
|
4383 |
+
if ($char_spacing != 0) {
|
4384 |
+
$w += $char_spacing * $space_scale * (count($unicode) + $n_spaces);
|
4385 |
+
}
|
4386 |
+
|
4387 |
+
} else {
|
4388 |
+
// If CPDF is in Unicode mode but the current font does not support Unicode we need to convert the character set to Windows-1252
|
4389 |
+
if ($this->isUnicode) {
|
4390 |
+
$text = mb_convert_encoding($text, 'Windows-1252', 'UTF-8');
|
4391 |
+
}
|
4392 |
+
|
4393 |
+
$len = mb_strlen($text, 'Windows-1252');
|
4394 |
+
|
4395 |
+
for ($i = 0; $i < $len; $i++) {
|
4396 |
+
$c = $text[$i];
|
4397 |
+
$char = isset($ord_cache[$c]) ? $ord_cache[$c] : ($ord_cache[$c] = ord($c));
|
4398 |
+
|
4399 |
+
// check if we have to replace character
|
4400 |
+
if (isset($current_font['differences'][$char])) {
|
4401 |
+
$char = $current_font['differences'][$char];
|
4402 |
+
}
|
4403 |
+
|
4404 |
+
if (isset($current_font['C'][$char])) {
|
4405 |
+
$char_width = $current_font['C'][$char];
|
4406 |
+
|
4407 |
+
// add the character width
|
4408 |
+
$w += $char_width;
|
4409 |
+
|
4410 |
+
// add additional padding for space
|
4411 |
+
if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space
|
4412 |
+
$w += $word_spacing * $space_scale;
|
4413 |
+
$n_spaces++;
|
4414 |
+
}
|
4415 |
+
}
|
4416 |
+
}
|
4417 |
+
|
4418 |
+
// add additional char spacing
|
4419 |
+
if ($char_spacing != 0) {
|
4420 |
+
$w += $char_spacing * $space_scale * ($len + $n_spaces);
|
4421 |
+
}
|
4422 |
+
}
|
4423 |
+
|
4424 |
+
$this->currentTextState = $store_currentTextState;
|
4425 |
+
$this->setCurrentFont();
|
4426 |
+
|
4427 |
+
return $w * $size / 1000;
|
4428 |
+
}
|
4429 |
+
|
4430 |
+
/**
|
4431 |
+
* this will be called at a new page to return the state to what it was on the
|
4432 |
+
* end of the previous page, before the stack was closed down
|
4433 |
+
* This is to get around not being able to have open 'q' across pages
|
4434 |
+
*
|
4435 |
+
* @param int $pageEnd
|
4436 |
+
*/
|
4437 |
+
function saveState($pageEnd = 0)
|
4438 |
+
{
|
4439 |
+
if ($pageEnd) {
|
4440 |
+
// this will be called at a new page to return the state to what it was on the
|
4441 |
+
// end of the previous page, before the stack was closed down
|
4442 |
+
// This is to get around not being able to have open 'q' across pages
|
4443 |
+
$opt = $this->stateStack[$pageEnd];
|
4444 |
+
// ok to use this as stack starts numbering at 1
|
4445 |
+
$this->setColor($opt['col'], true);
|
4446 |
+
$this->setStrokeColor($opt['str'], true);
|
4447 |
+
$this->addContent("\n" . $opt['lin']);
|
4448 |
+
// $this->currentLineStyle = $opt['lin'];
|
4449 |
+
} else {
|
4450 |
+
$this->nStateStack++;
|
4451 |
+
$this->stateStack[$this->nStateStack] = array(
|
4452 |
+
'col' => $this->currentColor,
|
4453 |
+
'str' => $this->currentStrokeColor,
|
4454 |
+
'lin' => $this->currentLineStyle
|
4455 |
+
);
|
4456 |
+
}
|
4457 |
+
|
4458 |
+
$this->save();
|
4459 |
+
}
|
4460 |
+
|
4461 |
+
/**
|
4462 |
+
* restore a previously saved state
|
4463 |
+
*
|
4464 |
+
* @param int $pageEnd
|
4465 |
+
*/
|
4466 |
+
function restoreState($pageEnd = 0)
|
4467 |
+
{
|
4468 |
+
if (!$pageEnd) {
|
4469 |
+
$n = $this->nStateStack;
|
4470 |
+
$this->currentColor = $this->stateStack[$n]['col'];
|
4471 |
+
$this->currentStrokeColor = $this->stateStack[$n]['str'];
|
4472 |
+
$this->addContent("\n" . $this->stateStack[$n]['lin']);
|
4473 |
+
$this->currentLineStyle = $this->stateStack[$n]['lin'];
|
4474 |
+
$this->stateStack[$n] = null;
|
4475 |
+
unset($this->stateStack[$n]);
|
4476 |
+
$this->nStateStack--;
|
4477 |
+
}
|
4478 |
+
|
4479 |
+
$this->restore();
|
4480 |
+
}
|
4481 |
+
|
4482 |
+
/**
|
4483 |
+
* make a loose object, the output will go into this object, until it is closed, then will revert to
|
4484 |
+
* the current one.
|
4485 |
+
* this object will not appear until it is included within a page.
|
4486 |
+
* the function will return the object number
|
4487 |
+
*
|
4488 |
+
* @return int
|
4489 |
+
*/
|
4490 |
+
function openObject()
|
4491 |
+
{
|
4492 |
+
$this->nStack++;
|
4493 |
+
$this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
|
4494 |
+
// add a new object of the content type, to hold the data flow
|
4495 |
+
$this->numObj++;
|
4496 |
+
$this->o_contents($this->numObj, 'new');
|
4497 |
+
$this->currentContents = $this->numObj;
|
4498 |
+
$this->looseObjects[$this->numObj] = 1;
|
4499 |
+
|
4500 |
+
return $this->numObj;
|
4501 |
+
}
|
4502 |
+
|
4503 |
+
/**
|
4504 |
+
* open an existing object for editing
|
4505 |
+
*
|
4506 |
+
* @param $id
|
4507 |
+
*/
|
4508 |
+
function reopenObject($id)
|
4509 |
+
{
|
4510 |
+
$this->nStack++;
|
4511 |
+
$this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
|
4512 |
+
$this->currentContents = $id;
|
4513 |
+
|
4514 |
+
// also if this object is the primary contents for a page, then set the current page to its parent
|
4515 |
+
if (isset($this->objects[$id]['onPage'])) {
|
4516 |
+
$this->currentPage = $this->objects[$id]['onPage'];
|
4517 |
+
}
|
4518 |
+
}
|
4519 |
+
|
4520 |
+
/**
|
4521 |
+
* close an object
|
4522 |
+
*/
|
4523 |
+
function closeObject()
|
4524 |
+
{
|
4525 |
+
// close the object, as long as there was one open in the first place, which will be indicated by
|
4526 |
+
// an objectId on the stack.
|
4527 |
+
if ($this->nStack > 0) {
|
4528 |
+
$this->currentContents = $this->stack[$this->nStack]['c'];
|
4529 |
+
$this->currentPage = $this->stack[$this->nStack]['p'];
|
4530 |
+
$this->nStack--;
|
4531 |
+
// easier to probably not worry about removing the old entries, they will be overwritten
|
4532 |
+
// if there are new ones.
|
4533 |
+
}
|
4534 |
+
}
|
4535 |
+
|
4536 |
+
/**
|
4537 |
+
* stop an object from appearing on pages from this point on
|
4538 |
+
*
|
4539 |
+
* @param $id
|
4540 |
+
*/
|
4541 |
+
function stopObject($id)
|
4542 |
+
{
|
4543 |
+
// if an object has been appearing on pages up to now, then stop it, this page will
|
4544 |
+
// be the last one that could contain it.
|
4545 |
+
if (isset($this->addLooseObjects[$id])) {
|
4546 |
+
$this->addLooseObjects[$id] = '';
|
4547 |
+
}
|
4548 |
+
}
|
4549 |
+
|
4550 |
+
/**
|
4551 |
+
* after an object has been created, it wil only show if it has been added, using this function.
|
4552 |
+
*
|
4553 |
+
* @param $id
|
4554 |
+
* @param string $options
|
4555 |
+
*/
|
4556 |
+
function addObject($id, $options = 'add')
|
4557 |
+
{
|
4558 |
+
// add the specified object to the page
|
4559 |
+
if (isset($this->looseObjects[$id]) && $this->currentContents != $id) {
|
4560 |
+
// then it is a valid object, and it is not being added to itself
|
4561 |
+
switch ($options) {
|
4562 |
+
case 'all':
|
4563 |
+
// then this object is to be added to this page (done in the next block) and
|
4564 |
+
// all future new pages.
|
4565 |
+
$this->addLooseObjects[$id] = 'all';
|
4566 |
+
|
4567 |
+
case 'add':
|
4568 |
+
if (isset($this->objects[$this->currentContents]['onPage'])) {
|
4569 |
+
// then the destination contents is the primary for the page
|
4570 |
+
// (though this object is actually added to that page)
|
4571 |
+
$this->o_page($this->objects[$this->currentContents]['onPage'], 'content', $id);
|
4572 |
+
}
|
4573 |
+
break;
|
4574 |
+
|
4575 |
+
case 'even':
|
4576 |
+
$this->addLooseObjects[$id] = 'even';
|
4577 |
+
$pageObjectId = $this->objects[$this->currentContents]['onPage'];
|
4578 |
+
if ($this->objects[$pageObjectId]['info']['pageNum'] % 2 == 0) {
|
4579 |
+
$this->addObject($id);
|
4580 |
+
// hacky huh :)
|
4581 |
+
}
|
4582 |
+
break;
|
4583 |
+
|
4584 |
+
case 'odd':
|
4585 |
+
$this->addLooseObjects[$id] = 'odd';
|
4586 |
+
$pageObjectId = $this->objects[$this->currentContents]['onPage'];
|
4587 |
+
if ($this->objects[$pageObjectId]['info']['pageNum'] % 2 == 1) {
|
4588 |
+
$this->addObject($id);
|
4589 |
+
// hacky huh :)
|
4590 |
+
}
|
4591 |
+
break;
|
4592 |
+
|
4593 |
+
case 'next':
|
4594 |
+
$this->addLooseObjects[$id] = 'all';
|
4595 |
+
break;
|
4596 |
+
|
4597 |
+
case 'nexteven':
|
4598 |
+
$this->addLooseObjects[$id] = 'even';
|
4599 |
+
break;
|
4600 |
+
|
4601 |
+
case 'nextodd':
|
4602 |
+
$this->addLooseObjects[$id] = 'odd';
|
4603 |
+
break;
|
4604 |
+
}
|
4605 |
+
}
|
4606 |
+
}
|
4607 |
+
|
4608 |
+
/**
|
4609 |
+
* return a storable representation of a specific object
|
4610 |
+
*
|
4611 |
+
* @param $id
|
4612 |
+
* @return string|null
|
4613 |
+
*/
|
4614 |
+
function serializeObject($id)
|
4615 |
+
{
|
4616 |
+
if (array_key_exists($id, $this->objects)) {
|
4617 |
+
return serialize($this->objects[$id]);
|
4618 |
+
}
|
4619 |
+
|
4620 |
+
return null;
|
4621 |
+
}
|
4622 |
+
|
4623 |
+
/**
|
4624 |
+
* restore an object from its stored representation. returns its new object id.
|
4625 |
+
*
|
4626 |
+
* @param $obj
|
4627 |
+
* @return int
|
4628 |
+
*/
|
4629 |
+
function restoreSerializedObject($obj)
|
4630 |
+
{
|
4631 |
+
$obj_id = $this->openObject();
|
4632 |
+
$this->objects[$obj_id] = unserialize($obj);
|
4633 |
+
$this->closeObject();
|
4634 |
+
|
4635 |
+
return $obj_id;
|
4636 |
+
}
|
4637 |
+
|
4638 |
+
/**
|
4639 |
+
* add content to the documents info object
|
4640 |
+
*
|
4641 |
+
* @param $label
|
4642 |
+
* @param int $value
|
4643 |
+
*/
|
4644 |
+
function addInfo($label, $value = 0)
|
4645 |
+
{
|
4646 |
+
// this will only work if the label is one of the valid ones.
|
4647 |
+
// modify this so that arrays can be passed as well.
|
4648 |
+
// if $label is an array then assume that it is key => value pairs
|
4649 |
+
// else assume that they are both scalar, anything else will probably error
|
4650 |
+
if (is_array($label)) {
|
4651 |
+
foreach ($label as $l => $v) {
|
4652 |
+
$this->o_info($this->infoObject, $l, $v);
|
4653 |
+
}
|
4654 |
+
} else {
|
4655 |
+
$this->o_info($this->infoObject, $label, $value);
|
4656 |
+
}
|
4657 |
+
}
|
4658 |
+
|
4659 |
+
/**
|
4660 |
+
* set the viewer preferences of the document, it is up to the browser to obey these.
|
4661 |
+
*
|
4662 |
+
* @param $label
|
4663 |
+
* @param int $value
|
4664 |
+
*/
|
4665 |
+
function setPreferences($label, $value = 0)
|
4666 |
+
{
|
4667 |
+
// this will only work if the label is one of the valid ones.
|
4668 |
+
if (is_array($label)) {
|
4669 |
+
foreach ($label as $l => $v) {
|
4670 |
+
$this->o_catalog($this->catalogId, 'viewerPreferences', array($l => $v));
|
4671 |
+
}
|
4672 |
+
} else {
|
4673 |
+
$this->o_catalog($this->catalogId, 'viewerPreferences', array($label => $value));
|
4674 |
+
}
|
4675 |
+
}
|
4676 |
+
|
4677 |
+
/**
|
4678 |
+
* extract an integer from a position in a byte stream
|
4679 |
+
*
|
4680 |
+
* @param $data
|
4681 |
+
* @param $pos
|
4682 |
+
* @param $num
|
4683 |
+
* @return int
|
4684 |
+
*/
|
4685 |
+
private function getBytes(&$data, $pos, $num)
|
4686 |
+
{
|
4687 |
+
// return the integer represented by $num bytes from $pos within $data
|
4688 |
+
$ret = 0;
|
4689 |
+
for ($i = 0; $i < $num; $i++) {
|
4690 |
+
$ret *= 256;
|
4691 |
+
$ret += ord($data[$pos + $i]);
|
4692 |
+
}
|
4693 |
+
|
4694 |
+
return $ret;
|
4695 |
+
}
|
4696 |
+
|
4697 |
+
/**
|
4698 |
+
* Check if image already added to pdf image directory.
|
4699 |
+
* If yes, need not to create again (pass empty data)
|
4700 |
+
*
|
4701 |
+
* @param $imgname
|
4702 |
+
* @return bool
|
4703 |
+
*/
|
4704 |
+
function image_iscached($imgname)
|
4705 |
+
{
|
4706 |
+
return isset($this->imagelist[$imgname]);
|
4707 |
+
}
|
4708 |
+
|
4709 |
+
/**
|
4710 |
+
* add a PNG image into the document, from a GD object
|
4711 |
+
* this should work with remote files
|
4712 |
+
*
|
4713 |
+
* @param string $file The PNG file
|
4714 |
+
* @param float $x X position
|
4715 |
+
* @param float $y Y position
|
4716 |
+
* @param float $w Width
|
4717 |
+
* @param float $h Height
|
4718 |
+
* @param resource $img A GD resource
|
4719 |
+
* @param bool $is_mask true if the image is a mask
|
4720 |
+
* @param bool $mask true if the image is masked
|
4721 |
+
* @throws Exception
|
4722 |
+
*/
|
4723 |
+
function addImagePng($file, $x, $y, $w = 0.0, $h = 0.0, &$img, $is_mask = false, $mask = null)
|
4724 |
+
{
|
4725 |
+
if (!function_exists("imagepng")) {
|
4726 |
+
throw new \Exception("The PHP GD extension is required, but is not installed.");
|
4727 |
+
}
|
4728 |
+
|
4729 |
+
//if already cached, need not to read again
|
4730 |
+
if (isset($this->imagelist[$file])) {
|
4731 |
+
$data = null;
|
4732 |
+
} else {
|
4733 |
+
// Example for transparency handling on new image. Retain for current image
|
4734 |
+
// $tIndex = imagecolortransparent($img);
|
4735 |
+
// if ($tIndex > 0) {
|
4736 |
+
// $tColor = imagecolorsforindex($img, $tIndex);
|
4737 |
+
// $new_tIndex = imagecolorallocate($new_img, $tColor['red'], $tColor['green'], $tColor['blue']);
|
4738 |
+
// imagefill($new_img, 0, 0, $new_tIndex);
|
4739 |
+
// imagecolortransparent($new_img, $new_tIndex);
|
4740 |
+
// }
|
4741 |
+
// blending mode (literal/blending) on drawing into current image. not relevant when not saved or not drawn
|
4742 |
+
//imagealphablending($img, true);
|
4743 |
+
|
4744 |
+
//default, but explicitely set to ensure pdf compatibility
|
4745 |
+
imagesavealpha($img, false/*!$is_mask && !$mask*/);
|
4746 |
+
|
4747 |
+
$error = 0;
|
4748 |
+
//DEBUG_IMG_TEMP
|
4749 |
+
//debugpng
|
4750 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
4751 |
+
print '[addImagePng ' . $file . ']';
|
4752 |
+
}
|
4753 |
+
|
4754 |
+
ob_start();
|
4755 |
+
@imagepng($img);
|
4756 |
+
$data = ob_get_clean();
|
4757 |
+
|
4758 |
+
if ($data == '') {
|
4759 |
+
$error = 1;
|
4760 |
+
$errormsg = 'trouble writing file from GD';
|
4761 |
+
//DEBUG_IMG_TEMP
|
4762 |
+
//debugpng
|
4763 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
4764 |
+
print 'trouble writing file from GD';
|
4765 |
+
}
|
4766 |
+
}
|
4767 |
+
|
4768 |
+
if ($error) {
|
4769 |
+
$this->addMessage('PNG error - (' . $file . ') ' . $errormsg);
|
4770 |
+
|
4771 |
+
return;
|
4772 |
+
}
|
4773 |
+
} //End isset($this->imagelist[$file]) (png Duplicate removal)
|
4774 |
+
|
4775 |
+
$this->addPngFromBuf($file, $x, $y, $w, $h, $data, $is_mask, $mask);
|
4776 |
+
}
|
4777 |
+
|
4778 |
+
/**
|
4779 |
+
* @param $file
|
4780 |
+
* @param $x
|
4781 |
+
* @param $y
|
4782 |
+
* @param $w
|
4783 |
+
* @param $h
|
4784 |
+
* @param $byte
|
4785 |
+
*/
|
4786 |
+
protected function addImagePngAlpha($file, $x, $y, $w, $h, $byte)
|
4787 |
+
{
|
4788 |
+
// generate images
|
4789 |
+
$img = imagecreatefrompng($file);
|
4790 |
+
|
4791 |
+
if ($img === false) {
|
4792 |
+
return;
|
4793 |
+
}
|
4794 |
+
|
4795 |
+
// FIXME The pixel transformation doesn't work well with 8bit PNGs
|
4796 |
+
$eight_bit = ($byte & 4) !== 4;
|
4797 |
+
|
4798 |
+
$wpx = imagesx($img);
|
4799 |
+
$hpx = imagesy($img);
|
4800 |
+
|
4801 |
+
imagesavealpha($img, false);
|
4802 |
+
|
4803 |
+
// create temp alpha file
|
4804 |
+
$tempfile_alpha = tempnam($this->tmp, "cpdf_img_");
|
4805 |
+
@unlink($tempfile_alpha);
|
4806 |
+
$tempfile_alpha = "$tempfile_alpha.png";
|
4807 |
+
|
4808 |
+
// create temp plain file
|
4809 |
+
$tempfile_plain = tempnam($this->tmp, "cpdf_img_");
|
4810 |
+
@unlink($tempfile_plain);
|
4811 |
+
$tempfile_plain = "$tempfile_plain.png";
|
4812 |
+
|
4813 |
+
$imgalpha = imagecreate($wpx, $hpx);
|
4814 |
+
imagesavealpha($imgalpha, false);
|
4815 |
+
|
4816 |
+
// generate gray scale palette (0 -> 255)
|
4817 |
+
for ($c = 0; $c < 256; ++$c) {
|
4818 |
+
imagecolorallocate($imgalpha, $c, $c, $c);
|
4819 |
+
}
|
4820 |
+
|
4821 |
+
// Use PECL gmagick + Graphics Magic to process transparent PNG images
|
4822 |
+
if (extension_loaded("gmagick")) {
|
4823 |
+
$gmagick = new \Gmagick($file);
|
4824 |
+
$gmagick->setimageformat('png');
|
4825 |
+
|
4826 |
+
// Get opacity channel (negative of alpha channel)
|
4827 |
+
$alpha_channel_neg = clone $gmagick;
|
4828 |
+
$alpha_channel_neg->separateimagechannel(\Gmagick::CHANNEL_OPACITY);
|
4829 |
+
|
4830 |
+
// Negate opacity channel
|
4831 |
+
$alpha_channel = new \Gmagick();
|
4832 |
+
$alpha_channel->newimage($wpx, $hpx, "#FFFFFF", "png");
|
4833 |
+
$alpha_channel->compositeimage($alpha_channel_neg, \Gmagick::COMPOSITE_DIFFERENCE, 0, 0);
|
4834 |
+
$alpha_channel->separateimagechannel(\Gmagick::CHANNEL_RED);
|
4835 |
+
$alpha_channel->writeimage($tempfile_alpha);
|
4836 |
+
|
4837 |
+
// Cast to 8bit+palette
|
4838 |
+
$imgalpha_ = imagecreatefrompng($tempfile_alpha);
|
4839 |
+
imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx);
|
4840 |
+
imagedestroy($imgalpha_);
|
4841 |
+
imagepng($imgalpha, $tempfile_alpha);
|
4842 |
+
|
4843 |
+
// Make opaque image
|
4844 |
+
$color_channels = new \Gmagick();
|
4845 |
+
$color_channels->newimage($wpx, $hpx, "#FFFFFF", "png");
|
4846 |
+
$color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYRED, 0, 0);
|
4847 |
+
$color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYGREEN, 0, 0);
|
4848 |
+
$color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYBLUE, 0, 0);
|
4849 |
+
$color_channels->writeimage($tempfile_plain);
|
4850 |
+
|
4851 |
+
$imgplain = imagecreatefrompng($tempfile_plain);
|
4852 |
+
}
|
4853 |
+
// Use PECL imagick + ImageMagic to process transparent PNG images
|
4854 |
+
elseif (extension_loaded("imagick")) {
|
4855 |
+
// Native cloning was added to pecl-imagick in svn commit 263814
|
4856 |
+
// the first version containing it was 3.0.1RC1
|
4857 |
+
static $imagickClonable = null;
|
4858 |
+
if ($imagickClonable === null) {
|
4859 |
+
$imagickClonable = version_compare(Imagick::IMAGICK_EXTVER, '3.0.1rc1') > 0;
|
4860 |
+
}
|
4861 |
+
|
4862 |
+
$imagick = new \Imagick($file);
|
4863 |
+
$imagick->setFormat('png');
|
4864 |
+
|
4865 |
+
// Get opacity channel (negative of alpha channel)
|
4866 |
+
if ($imagick->getImageAlphaChannel() !== 0) {
|
4867 |
+
$alpha_channel = $imagickClonable ? clone $imagick : $imagick->clone();
|
4868 |
+
$alpha_channel->separateImageChannel(\Imagick::CHANNEL_ALPHA);
|
4869 |
+
$alpha_channel->negateImage(true);
|
4870 |
+
$alpha_channel->writeImage($tempfile_alpha);
|
4871 |
+
|
4872 |
+
// Cast to 8bit+palette
|
4873 |
+
$imgalpha_ = imagecreatefrompng($tempfile_alpha);
|
4874 |
+
imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx);
|
4875 |
+
imagedestroy($imgalpha_);
|
4876 |
+
imagepng($imgalpha, $tempfile_alpha);
|
4877 |
+
} else {
|
4878 |
+
$tempfile_alpha = null;
|
4879 |
+
}
|
4880 |
+
|
4881 |
+
// Make opaque image
|
4882 |
+
$color_channels = new \Imagick();
|
4883 |
+
$color_channels->newImage($wpx, $hpx, "#FFFFFF", "png");
|
4884 |
+
$color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYRED, 0, 0);
|
4885 |
+
$color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYGREEN, 0, 0);
|
4886 |
+
$color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYBLUE, 0, 0);
|
4887 |
+
$color_channels->writeImage($tempfile_plain);
|
4888 |
+
|
4889 |
+
$imgplain = imagecreatefrompng($tempfile_plain);
|
4890 |
+
} else {
|
4891 |
+
// allocated colors cache
|
4892 |
+
$allocated_colors = array();
|
4893 |
+
|
4894 |
+
// extract alpha channel
|
4895 |
+
for ($xpx = 0; $xpx < $wpx; ++$xpx) {
|
4896 |
+
for ($ypx = 0; $ypx < $hpx; ++$ypx) {
|
4897 |
+
$color = imagecolorat($img, $xpx, $ypx);
|
4898 |
+
$col = imagecolorsforindex($img, $color);
|
4899 |
+
$alpha = $col['alpha'];
|
4900 |
+
|
4901 |
+
if ($eight_bit) {
|
4902 |
+
// with gamma correction
|
4903 |
+
$gammacorr = 2.2;
|
4904 |
+
$pixel = pow((((127 - $alpha) * 255 / 127) / 255), $gammacorr) * 255;
|
4905 |
+
} else {
|
4906 |
+
// without gamma correction
|
4907 |
+
$pixel = (127 - $alpha) * 2;
|
4908 |
+
|
4909 |
+
$key = $col['red'] . $col['green'] . $col['blue'];
|
4910 |
+
|
4911 |
+
if (!isset($allocated_colors[$key])) {
|
4912 |
+
$pixel_img = imagecolorallocate($img, $col['red'], $col['green'], $col['blue']);
|
4913 |
+
$allocated_colors[$key] = $pixel_img;
|
4914 |
+
} else {
|
4915 |
+
$pixel_img = $allocated_colors[$key];
|
4916 |
+
}
|
4917 |
+
|
4918 |
+
imagesetpixel($img, $xpx, $ypx, $pixel_img);
|
4919 |
+
}
|
4920 |
+
|
4921 |
+
imagesetpixel($imgalpha, $xpx, $ypx, $pixel);
|
4922 |
+
}
|
4923 |
+
}
|
4924 |
+
|
4925 |
+
// extract image without alpha channel
|
4926 |
+
$imgplain = imagecreatetruecolor($wpx, $hpx);
|
4927 |
+
imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
|
4928 |
+
imagedestroy($img);
|
4929 |
+
|
4930 |
+
imagepng($imgalpha, $tempfile_alpha);
|
4931 |
+
imagepng($imgplain, $tempfile_plain);
|
4932 |
+
}
|
4933 |
+
|
4934 |
+
// embed mask image
|
4935 |
+
if ($tempfile_alpha) {
|
4936 |
+
$this->addImagePng($tempfile_alpha, $x, $y, $w, $h, $imgalpha, true);
|
4937 |
+
imagedestroy($imgalpha);
|
4938 |
+
}
|
4939 |
+
|
4940 |
+
// embed image, masked with previously embedded mask
|
4941 |
+
$this->addImagePng($tempfile_plain, $x, $y, $w, $h, $imgplain, false, ($tempfile_alpha !== null));
|
4942 |
+
imagedestroy($imgplain);
|
4943 |
+
|
4944 |
+
// remove temp files
|
4945 |
+
if ($tempfile_alpha) {
|
4946 |
+
unlink($tempfile_alpha);
|
4947 |
+
}
|
4948 |
+
unlink($tempfile_plain);
|
4949 |
+
}
|
4950 |
+
|
4951 |
+
/**
|
4952 |
+
* add a PNG image into the document, from a file
|
4953 |
+
* this should work with remote files
|
4954 |
+
*
|
4955 |
+
* @param $file
|
4956 |
+
* @param $x
|
4957 |
+
* @param $y
|
4958 |
+
* @param int $w
|
4959 |
+
* @param int $h
|
4960 |
+
* @throws Exception
|
4961 |
+
*/
|
4962 |
+
function addPngFromFile($file, $x, $y, $w = 0, $h = 0)
|
4963 |
+
{
|
4964 |
+
if (!function_exists("imagecreatefrompng")) {
|
4965 |
+
throw new \Exception("The PHP GD extension is required, but is not installed.");
|
4966 |
+
}
|
4967 |
+
|
4968 |
+
//if already cached, need not to read again
|
4969 |
+
if (isset($this->imagelist[$file])) {
|
4970 |
+
$img = null;
|
4971 |
+
} else {
|
4972 |
+
$info = file_get_contents($file, false, null, 24, 5);
|
4973 |
+
$meta = unpack("CbitDepth/CcolorType/CcompressionMethod/CfilterMethod/CinterlaceMethod", $info);
|
4974 |
+
$bit_depth = $meta["bitDepth"];
|
4975 |
+
$color_type = $meta["colorType"];
|
4976 |
+
|
4977 |
+
// http://www.w3.org/TR/PNG/#11IHDR
|
4978 |
+
// 3 => indexed
|
4979 |
+
// 4 => greyscale with alpha
|
4980 |
+
// 6 => fullcolor with alpha
|
4981 |
+
$is_alpha = in_array($color_type, array(4, 6)) || ($color_type == 3 && $bit_depth != 4);
|
4982 |
+
|
4983 |
+
if ($is_alpha) { // exclude grayscale alpha
|
4984 |
+
$this->addImagePngAlpha($file, $x, $y, $w, $h, $color_type);
|
4985 |
+
return;
|
4986 |
+
}
|
4987 |
+
|
4988 |
+
//png files typically contain an alpha channel.
|
4989 |
+
//pdf file format or class.pdf does not support alpha blending.
|
4990 |
+
//on alpha blended images, more transparent areas have a color near black.
|
4991 |
+
//This appears in the result on not storing the alpha channel.
|
4992 |
+
//Correct would be the box background image or its parent when transparent.
|
4993 |
+
//But this would make the image dependent on the background.
|
4994 |
+
//Therefore create an image with white background and copy in
|
4995 |
+
//A more natural background than black is white.
|
4996 |
+
//Therefore create an empty image with white background and merge the
|
4997 |
+
//image in with alpha blending.
|
4998 |
+
$imgtmp = @imagecreatefrompng($file);
|
4999 |
+
if (!$imgtmp) {
|
5000 |
+
return;
|
5001 |
+
}
|
5002 |
+
$sx = imagesx($imgtmp);
|
5003 |
+
$sy = imagesy($imgtmp);
|
5004 |
+
$img = imagecreatetruecolor($sx, $sy);
|
5005 |
+
imagealphablending($img, true);
|
5006 |
+
|
5007 |
+
// @todo is it still needed ??
|
5008 |
+
$ti = imagecolortransparent($imgtmp);
|
5009 |
+
if ($ti >= 0) {
|
5010 |
+
$tc = imagecolorsforindex($imgtmp, $ti);
|
5011 |
+
$ti = imagecolorallocate($img, $tc['red'], $tc['green'], $tc['blue']);
|
5012 |
+
imagefill($img, 0, 0, $ti);
|
5013 |
+
imagecolortransparent($img, $ti);
|
5014 |
+
} else {
|
5015 |
+
imagefill($img, 1, 1, imagecolorallocate($img, 255, 255, 255));
|
5016 |
+
}
|
5017 |
+
|
5018 |
+
imagecopy($img, $imgtmp, 0, 0, 0, 0, $sx, $sy);
|
5019 |
+
imagedestroy($imgtmp);
|
5020 |
+
}
|
5021 |
+
$this->addImagePng($file, $x, $y, $w, $h, $img);
|
5022 |
+
|
5023 |
+
if ($img) {
|
5024 |
+
imagedestroy($img);
|
5025 |
+
}
|
5026 |
+
}
|
5027 |
+
|
5028 |
+
/**
|
5029 |
+
* add a PNG image into the document, from a file
|
5030 |
+
* this should work with remote files
|
5031 |
+
*
|
5032 |
+
* @param $file
|
5033 |
+
* @param $x
|
5034 |
+
* @param $y
|
5035 |
+
* @param int $w
|
5036 |
+
* @param int $h
|
5037 |
+
*/
|
5038 |
+
function addSvgFromFile($file, $x, $y, $w = 0, $h = 0)
|
5039 |
+
{
|
5040 |
+
$doc = new \Svg\Document();
|
5041 |
+
$doc->loadFile($file);
|
5042 |
+
$dimensions = $doc->getDimensions();
|
5043 |
+
|
5044 |
+
$this->save();
|
5045 |
+
|
5046 |
+
$this->transform(array($w / $dimensions["width"], 0, 0, $h / $dimensions["height"], $x, $y));
|
5047 |
+
|
5048 |
+
$surface = new \Svg\Surface\SurfaceCpdf($doc, $this);
|
5049 |
+
$doc->render($surface);
|
5050 |
+
|
5051 |
+
$this->restore();
|
5052 |
+
}
|
5053 |
+
|
5054 |
+
/**
|
5055 |
+
* add a PNG image into the document, from a memory buffer of the file
|
5056 |
+
*
|
5057 |
+
* @param $file
|
5058 |
+
* @param $x
|
5059 |
+
* @param $y
|
5060 |
+
* @param float $w
|
5061 |
+
* @param float $h
|
5062 |
+
* @param $data
|
5063 |
+
* @param bool $is_mask
|
5064 |
+
* @param null $mask
|
5065 |
+
*/
|
5066 |
+
function addPngFromBuf($file, $x, $y, $w = 0.0, $h = 0.0, &$data, $is_mask = false, $mask = null)
|
5067 |
+
{
|
5068 |
+
if (isset($this->imagelist[$file])) {
|
5069 |
+
$data = null;
|
5070 |
+
$info['width'] = $this->imagelist[$file]['w'];
|
5071 |
+
$info['height'] = $this->imagelist[$file]['h'];
|
5072 |
+
$label = $this->imagelist[$file]['label'];
|
5073 |
+
} else {
|
5074 |
+
if ($data == null) {
|
5075 |
+
$this->addMessage('addPngFromBuf error - data not present!');
|
5076 |
+
|
5077 |
+
return;
|
5078 |
+
}
|
5079 |
+
|
5080 |
+
$error = 0;
|
5081 |
+
|
5082 |
+
if (!$error) {
|
5083 |
+
$header = chr(137) . chr(80) . chr(78) . chr(71) . chr(13) . chr(10) . chr(26) . chr(10);
|
5084 |
+
|
5085 |
+
if (mb_substr($data, 0, 8, '8bit') != $header) {
|
5086 |
+
$error = 1;
|
5087 |
+
|
5088 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5089 |
+
print '[addPngFromFile this file does not have a valid header ' . $file . ']';
|
5090 |
+
}
|
5091 |
+
|
5092 |
+
$errormsg = 'this file does not have a valid header';
|
5093 |
+
}
|
5094 |
+
}
|
5095 |
+
|
5096 |
+
if (!$error) {
|
5097 |
+
// set pointer
|
5098 |
+
$p = 8;
|
5099 |
+
$len = mb_strlen($data, '8bit');
|
5100 |
+
|
5101 |
+
// cycle through the file, identifying chunks
|
5102 |
+
$haveHeader = 0;
|
5103 |
+
$info = array();
|
5104 |
+
$idata = '';
|
5105 |
+
$pdata = '';
|
5106 |
+
|
5107 |
+
while ($p < $len) {
|
5108 |
+
$chunkLen = $this->getBytes($data, $p, 4);
|
5109 |
+
$chunkType = mb_substr($data, $p + 4, 4, '8bit');
|
5110 |
+
|
5111 |
+
switch ($chunkType) {
|
5112 |
+
case 'IHDR':
|
5113 |
+
// this is where all the file information comes from
|
5114 |
+
$info['width'] = $this->getBytes($data, $p + 8, 4);
|
5115 |
+
$info['height'] = $this->getBytes($data, $p + 12, 4);
|
5116 |
+
$info['bitDepth'] = ord($data[$p + 16]);
|
5117 |
+
$info['colorType'] = ord($data[$p + 17]);
|
5118 |
+
$info['compressionMethod'] = ord($data[$p + 18]);
|
5119 |
+
$info['filterMethod'] = ord($data[$p + 19]);
|
5120 |
+
$info['interlaceMethod'] = ord($data[$p + 20]);
|
5121 |
+
|
5122 |
+
//print_r($info);
|
5123 |
+
$haveHeader = 1;
|
5124 |
+
if ($info['compressionMethod'] != 0) {
|
5125 |
+
$error = 1;
|
5126 |
+
|
5127 |
+
//debugpng
|
5128 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5129 |
+
print '[addPngFromFile unsupported compression method ' . $file . ']';
|
5130 |
+
}
|
5131 |
+
|
5132 |
+
$errormsg = 'unsupported compression method';
|
5133 |
+
}
|
5134 |
+
|
5135 |
+
if ($info['filterMethod'] != 0) {
|
5136 |
+
$error = 1;
|
5137 |
+
|
5138 |
+
//debugpng
|
5139 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5140 |
+
print '[addPngFromFile unsupported filter method ' . $file . ']';
|
5141 |
+
}
|
5142 |
+
|
5143 |
+
$errormsg = 'unsupported filter method';
|
5144 |
+
}
|
5145 |
+
break;
|
5146 |
+
|
5147 |
+
case 'PLTE':
|
5148 |
+
$pdata .= mb_substr($data, $p + 8, $chunkLen, '8bit');
|
5149 |
+
break;
|
5150 |
+
|
5151 |
+
case 'IDAT':
|
5152 |
+
$idata .= mb_substr($data, $p + 8, $chunkLen, '8bit');
|
5153 |
+
break;
|
5154 |
+
|
5155 |
+
case 'tRNS':
|
5156 |
+
//this chunk can only occur once and it must occur after the PLTE chunk and before IDAT chunk
|
5157 |
+
//print "tRNS found, color type = ".$info['colorType']."\n";
|
5158 |
+
$transparency = array();
|
5159 |
+
|
5160 |
+
switch ($info['colorType']) {
|
5161 |
+
// indexed color, rbg
|
5162 |
+
case 3:
|
5163 |
+
/* corresponding to entries in the plte chunk
|
5164 |
+
Alpha for palette index 0: 1 byte
|
5165 |
+
Alpha for palette index 1: 1 byte
|
5166 |
+
...etc...
|
5167 |
+
*/
|
5168 |
+
// there will be one entry for each palette entry. up until the last non-opaque entry.
|
5169 |
+
// set up an array, stretching over all palette entries which will be o (opaque) or 1 (transparent)
|
5170 |
+
$transparency['type'] = 'indexed';
|
5171 |
+
$trans = 0;
|
5172 |
+
|
5173 |
+
for ($i = $chunkLen; $i >= 0; $i--) {
|
5174 |
+
if (ord($data[$p + 8 + $i]) == 0) {
|
5175 |
+
$trans = $i;
|
5176 |
+
}
|
5177 |
+
}
|
5178 |
+
|
5179 |
+
$transparency['data'] = $trans;
|
5180 |
+
break;
|
5181 |
+
|
5182 |
+
// grayscale
|
5183 |
+
case 0:
|
5184 |
+
/* corresponding to entries in the plte chunk
|
5185 |
+
Gray: 2 bytes, range 0 .. (2^bitdepth)-1
|
5186 |
+
*/
|
5187 |
+
// $transparency['grayscale'] = $this->PRVT_getBytes($data,$p+8,2); // g = grayscale
|
5188 |
+
$transparency['type'] = 'indexed';
|
5189 |
+
$transparency['data'] = ord($data[$p + 8 + 1]);
|
5190 |
+
break;
|
5191 |
+
|
5192 |
+
// truecolor
|
5193 |
+
case 2:
|
5194 |
+
/* corresponding to entries in the plte chunk
|
5195 |
+
Red: 2 bytes, range 0 .. (2^bitdepth)-1
|
5196 |
+
Green: 2 bytes, range 0 .. (2^bitdepth)-1
|
5197 |
+
Blue: 2 bytes, range 0 .. (2^bitdepth)-1
|
5198 |
+
*/
|
5199 |
+
$transparency['r'] = $this->getBytes($data, $p + 8, 2);
|
5200 |
+
// r from truecolor
|
5201 |
+
$transparency['g'] = $this->getBytes($data, $p + 10, 2);
|
5202 |
+
// g from truecolor
|
5203 |
+
$transparency['b'] = $this->getBytes($data, $p + 12, 2);
|
5204 |
+
// b from truecolor
|
5205 |
+
|
5206 |
+
$transparency['type'] = 'color-key';
|
5207 |
+
break;
|
5208 |
+
|
5209 |
+
//unsupported transparency type
|
5210 |
+
default:
|
5211 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5212 |
+
print '[addPngFromFile unsupported transparency type ' . $file . ']';
|
5213 |
+
}
|
5214 |
+
break;
|
5215 |
+
}
|
5216 |
+
|
5217 |
+
// KS End new code
|
5218 |
+
break;
|
5219 |
+
|
5220 |
+
default:
|
5221 |
+
break;
|
5222 |
+
}
|
5223 |
+
|
5224 |
+
$p += $chunkLen + 12;
|
5225 |
+
}
|
5226 |
+
|
5227 |
+
if (!$haveHeader) {
|
5228 |
+
$error = 1;
|
5229 |
+
|
5230 |
+
//debugpng
|
5231 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5232 |
+
print '[addPngFromFile information header is missing ' . $file . ']';
|
5233 |
+
}
|
5234 |
+
|
5235 |
+
$errormsg = 'information header is missing';
|
5236 |
+
}
|
5237 |
+
|
5238 |
+
if (isset($info['interlaceMethod']) && $info['interlaceMethod']) {
|
5239 |
+
$error = 1;
|
5240 |
+
|
5241 |
+
//debugpng
|
5242 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5243 |
+
print '[addPngFromFile no support for interlaced images in pdf ' . $file . ']';
|
5244 |
+
}
|
5245 |
+
|
5246 |
+
$errormsg = 'There appears to be no support for interlaced images in pdf.';
|
5247 |
+
}
|
5248 |
+
}
|
5249 |
+
|
5250 |
+
if (!$error && $info['bitDepth'] > 8) {
|
5251 |
+
$error = 1;
|
5252 |
+
|
5253 |
+
//debugpng
|
5254 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5255 |
+
print '[addPngFromFile bit depth of 8 or less is supported ' . $file . ']';
|
5256 |
+
}
|
5257 |
+
|
5258 |
+
$errormsg = 'only bit depth of 8 or less is supported';
|
5259 |
+
}
|
5260 |
+
|
5261 |
+
if (!$error) {
|
5262 |
+
switch ($info['colorType']) {
|
5263 |
+
case 3:
|
5264 |
+
$color = 'DeviceRGB';
|
5265 |
+
$ncolor = 1;
|
5266 |
+
break;
|
5267 |
+
|
5268 |
+
case 2:
|
5269 |
+
$color = 'DeviceRGB';
|
5270 |
+
$ncolor = 3;
|
5271 |
+
break;
|
5272 |
+
|
5273 |
+
case 0:
|
5274 |
+
$color = 'DeviceGray';
|
5275 |
+
$ncolor = 1;
|
5276 |
+
break;
|
5277 |
+
|
5278 |
+
default:
|
5279 |
+
$error = 1;
|
5280 |
+
|
5281 |
+
//debugpng
|
5282 |
+
if (defined("DEBUGPNG") && DEBUGPNG) {
|
5283 |
+
print '[addPngFromFile alpha channel not supported: ' . $info['colorType'] . ' ' . $file . ']';
|
5284 |
+
}
|
5285 |
+
|
5286 |
+
$errormsg = 'transparency alpha channel not supported, transparency only supported for palette images.';
|
5287 |
+
}
|
5288 |
+
}
|
5289 |
+
|
5290 |
+
if ($error) {
|
5291 |
+
$this->addMessage('PNG error - (' . $file . ') ' . $errormsg);
|
5292 |
+
|
5293 |
+
return;
|
5294 |
+
}
|
5295 |
+
|
5296 |
+
//print_r($info);
|
5297 |
+
// so this image is ok... add it in.
|
5298 |
+
$this->numImages++;
|
5299 |
+
$im = $this->numImages;
|
5300 |
+
$label = "I$im";
|
5301 |
+
$this->numObj++;
|
5302 |
+
|
5303 |
+
// $this->o_image($this->numObj,'new',array('label' => $label,'data' => $idata,'iw' => $w,'ih' => $h,'type' => 'png','ic' => $info['width']));
|
5304 |
+
$options = array(
|
5305 |
+
'label' => $label,
|
5306 |
+
'data' => $idata,
|
5307 |
+
'bitsPerComponent' => $info['bitDepth'],
|
5308 |
+
'pdata' => $pdata,
|
5309 |
+
'iw' => $info['width'],
|
5310 |
+
'ih' => $info['height'],
|
5311 |
+
'type' => 'png',
|
5312 |
+
'color' => $color,
|
5313 |
+
'ncolor' => $ncolor,
|
5314 |
+
'masked' => $mask,
|
5315 |
+
'isMask' => $is_mask
|
5316 |
+
);
|
5317 |
+
|
5318 |
+
if (isset($transparency)) {
|
5319 |
+
$options['transparency'] = $transparency;
|
5320 |
+
}
|
5321 |
+
|
5322 |
+
$this->o_image($this->numObj, 'new', $options);
|
5323 |
+
$this->imagelist[$file] = array('label' => $label, 'w' => $info['width'], 'h' => $info['height']);
|
5324 |
+
}
|
5325 |
+
|
5326 |
+
if ($is_mask) {
|
5327 |
+
return;
|
5328 |
+
}
|
5329 |
+
|
5330 |
+
if ($w <= 0 && $h <= 0) {
|
5331 |
+
$w = $info['width'];
|
5332 |
+
$h = $info['height'];
|
5333 |
+
}
|
5334 |
+
|
5335 |
+
if ($w <= 0) {
|
5336 |
+
$w = $h / $info['height'] * $info['width'];
|
5337 |
+
}
|
5338 |
+
|
5339 |
+
if ($h <= 0) {
|
5340 |
+
$h = $w * $info['height'] / $info['width'];
|
5341 |
+
}
|
5342 |
+
|
5343 |
+
$this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ", $w, $h, $x, $y, $label));
|
5344 |
+
}
|
5345 |
+
|
5346 |
+
/**
|
5347 |
+
* add a JPEG image into the document, from a file
|
5348 |
+
*
|
5349 |
+
* @param $img
|
5350 |
+
* @param $x
|
5351 |
+
* @param $y
|
5352 |
+
* @param int $w
|
5353 |
+
* @param int $h
|
5354 |
+
*/
|
5355 |
+
function addJpegFromFile($img, $x, $y, $w = 0, $h = 0)
|
5356 |
+
{
|
5357 |
+
// attempt to add a jpeg image straight from a file, using no GD commands
|
5358 |
+
// note that this function is unable to operate on a remote file.
|
5359 |
+
|
5360 |
+
if (!file_exists($img)) {
|
5361 |
+
return;
|
5362 |
+
}
|
5363 |
+
|
5364 |
+
if ($this->image_iscached($img)) {
|
5365 |
+
$data = null;
|
5366 |
+
$imageWidth = $this->imagelist[$img]['w'];
|
5367 |
+
$imageHeight = $this->imagelist[$img]['h'];
|
5368 |
+
$channels = $this->imagelist[$img]['c'];
|
5369 |
+
} else {
|
5370 |
+
$tmp = getimagesize($img);
|
5371 |
+
$imageWidth = $tmp[0];
|
5372 |
+
$imageHeight = $tmp[1];
|
5373 |
+
|
5374 |
+
if (isset($tmp['channels'])) {
|
5375 |
+
$channels = $tmp['channels'];
|
5376 |
+
} else {
|
5377 |
+
$channels = 3;
|
5378 |
+
}
|
5379 |
+
|
5380 |
+
$data = file_get_contents($img);
|
5381 |
+
}
|
5382 |
+
|
5383 |
+
if ($w <= 0 && $h <= 0) {
|
5384 |
+
$w = $imageWidth;
|
5385 |
+
}
|
5386 |
+
|
5387 |
+
if ($w == 0) {
|
5388 |
+
$w = $h / $imageHeight * $imageWidth;
|
5389 |
+
}
|
5390 |
+
|
5391 |
+
if ($h == 0) {
|
5392 |
+
$h = $w * $imageHeight / $imageWidth;
|
5393 |
+
}
|
5394 |
+
|
5395 |
+
$this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight, $channels, $img);
|
5396 |
+
}
|
5397 |
+
|
5398 |
+
/**
|
5399 |
+
* common code used by the two JPEG adding functions
|
5400 |
+
* @param $data
|
5401 |
+
* @param $x
|
5402 |
+
* @param $y
|
5403 |
+
* @param int $w
|
5404 |
+
* @param int $h
|
5405 |
+
* @param $imageWidth
|
5406 |
+
* @param $imageHeight
|
5407 |
+
* @param int $channels
|
5408 |
+
* @param $imgname
|
5409 |
+
*/
|
5410 |
+
private function addJpegImage_common(
|
5411 |
+
&$data,
|
5412 |
+
$x,
|
5413 |
+
$y,
|
5414 |
+
$w = 0,
|
5415 |
+
$h = 0,
|
5416 |
+
$imageWidth,
|
5417 |
+
$imageHeight,
|
5418 |
+
$channels = 3,
|
5419 |
+
$imgname
|
5420 |
+
) {
|
5421 |
+
if ($this->image_iscached($imgname)) {
|
5422 |
+
$label = $this->imagelist[$imgname]['label'];
|
5423 |
+
//debugpng
|
5424 |
+
//if (DEBUGPNG) print '[addJpegImage_common Duplicate '.$imgname.']';
|
5425 |
+
|
5426 |
+
} else {
|
5427 |
+
if ($data == null) {
|
5428 |
+
$this->addMessage('addJpegImage_common error - (' . $imgname . ') data not present!');
|
5429 |
+
|
5430 |
+
return;
|
5431 |
+
}
|
5432 |
+
|
5433 |
+
// note that this function is not to be called externally
|
5434 |
+
// it is just the common code between the GD and the file options
|
5435 |
+
$this->numImages++;
|
5436 |
+
$im = $this->numImages;
|
5437 |
+
$label = "I$im";
|
5438 |
+
$this->numObj++;
|
5439 |
+
|
5440 |
+
$this->o_image(
|
5441 |
+
$this->numObj,
|
5442 |
+
'new',
|
5443 |
+
array(
|
5444 |
+
'label' => $label,
|
5445 |
+
'data' => &$data,
|
5446 |
+
'iw' => $imageWidth,
|
5447 |
+
'ih' => $imageHeight,
|
5448 |
+
'channels' => $channels
|
5449 |
+
)
|
5450 |
+
);
|
5451 |
+
|
5452 |
+
$this->imagelist[$imgname] = array(
|
5453 |
+
'label' => $label,
|
5454 |
+
'w' => $imageWidth,
|
5455 |
+
'h' => $imageHeight,
|
5456 |
+
'c' => $channels
|
5457 |
+
);
|
5458 |
+
}
|
5459 |
+
|
5460 |
+
$this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ ", $w, $h, $x, $y, $label));
|
5461 |
+
}
|
5462 |
+
|
5463 |
+
/**
|
5464 |
+
* specify where the document should open when it first starts
|
5465 |
+
*
|
5466 |
+
* @param $style
|
5467 |
+
* @param int $a
|
5468 |
+
* @param int $b
|
5469 |
+
* @param int $c
|
5470 |
+
*/
|
5471 |
+
function openHere($style, $a = 0, $b = 0, $c = 0)
|
5472 |
+
{
|
5473 |
+
// this function will open the document at a specified page, in a specified style
|
5474 |
+
// the values for style, and the required parameters are:
|
5475 |
+
// 'XYZ' left, top, zoom
|
5476 |
+
// 'Fit'
|
5477 |
+
// 'FitH' top
|
5478 |
+
// 'FitV' left
|
5479 |
+
// 'FitR' left,bottom,right
|
5480 |
+
// 'FitB'
|
5481 |
+
// 'FitBH' top
|
5482 |
+
// 'FitBV' left
|
5483 |
+
$this->numObj++;
|
5484 |
+
$this->o_destination(
|
5485 |
+
$this->numObj,
|
5486 |
+
'new',
|
5487 |
+
array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c)
|
5488 |
+
);
|
5489 |
+
$id = $this->catalogId;
|
5490 |
+
$this->o_catalog($id, 'openHere', $this->numObj);
|
5491 |
+
}
|
5492 |
+
|
5493 |
+
/**
|
5494 |
+
* Add JavaScript code to the PDF document
|
5495 |
+
*
|
5496 |
+
* @param string $code
|
5497 |
+
*/
|
5498 |
+
function addJavascript($code)
|
5499 |
+
{
|
5500 |
+
$this->javascript .= $code;
|
5501 |
+
}
|
5502 |
+
|
5503 |
+
/**
|
5504 |
+
* create a labelled destination within the document
|
5505 |
+
*
|
5506 |
+
* @param $label
|
5507 |
+
* @param $style
|
5508 |
+
* @param int $a
|
5509 |
+
* @param int $b
|
5510 |
+
* @param int $c
|
5511 |
+
*/
|
5512 |
+
function addDestination($label, $style, $a = 0, $b = 0, $c = 0)
|
5513 |
+
{
|
5514 |
+
// associates the given label with the destination, it is done this way so that a destination can be specified after
|
5515 |
+
// it has been linked to
|
5516 |
+
// styles are the same as the 'openHere' function
|
5517 |
+
$this->numObj++;
|
5518 |
+
$this->o_destination(
|
5519 |
+
$this->numObj,
|
5520 |
+
'new',
|
5521 |
+
array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c)
|
5522 |
+
);
|
5523 |
+
$id = $this->numObj;
|
5524 |
+
|
5525 |
+
// store the label->idf relationship, note that this means that labels can be used only once
|
5526 |
+
$this->destinations["$label"] = $id;
|
5527 |
+
}
|
5528 |
+
|
5529 |
+
/**
|
5530 |
+
* define font families, this is used to initialize the font families for the default fonts
|
5531 |
+
* and for the user to add new ones for their fonts. The default bahavious can be overridden should
|
5532 |
+
* that be desired.
|
5533 |
+
*
|
5534 |
+
* @param $family
|
5535 |
+
* @param string $options
|
5536 |
+
*/
|
5537 |
+
function setFontFamily($family, $options = '')
|
5538 |
+
{
|
5539 |
+
if (!is_array($options)) {
|
5540 |
+
if ($family === 'init') {
|
5541 |
+
// set the known family groups
|
5542 |
+
// these font families will be used to enable bold and italic markers to be included
|
5543 |
+
// within text streams. html forms will be used... <b></b> <i></i>
|
5544 |
+
$this->fontFamilies['Helvetica.afm'] =
|
5545 |
+
array(
|
5546 |
+
'b' => 'Helvetica-Bold.afm',
|
5547 |
+
'i' => 'Helvetica-Oblique.afm',
|
5548 |
+
'bi' => 'Helvetica-BoldOblique.afm',
|
5549 |
+
'ib' => 'Helvetica-BoldOblique.afm'
|
5550 |
+
);
|
5551 |
+
|
5552 |
+
$this->fontFamilies['Courier.afm'] =
|
5553 |
+
array(
|
5554 |
+
'b' => 'Courier-Bold.afm',
|
5555 |
+
'i' => 'Courier-Oblique.afm',
|
5556 |
+
'bi' => 'Courier-BoldOblique.afm',
|
5557 |
+
'ib' => 'Courier-BoldOblique.afm'
|
5558 |
+
);
|
5559 |
+
|
5560 |
+
$this->fontFamilies['Times-Roman.afm'] =
|
5561 |
+
array(
|
5562 |
+
'b' => 'Times-Bold.afm',
|
5563 |
+
'i' => 'Times-Italic.afm',
|
5564 |
+
'bi' => 'Times-BoldItalic.afm',
|
5565 |
+
'ib' => 'Times-BoldItalic.afm'
|
5566 |
+
);
|
5567 |
+
}
|
5568 |
+
} else {
|
5569 |
+
|
5570 |
+
// the user is trying to set a font family
|
5571 |
+
// note that this can also be used to set the base ones to something else
|
5572 |
+
if (mb_strlen($family)) {
|
5573 |
+
$this->fontFamilies[$family] = $options;
|
5574 |
+
}
|
5575 |
+
}
|
5576 |
+
}
|
5577 |
+
|
5578 |
+
/**
|
5579 |
+
* used to add messages for use in debugging
|
5580 |
+
*
|
5581 |
+
* @param $message
|
5582 |
+
*/
|
5583 |
+
function addMessage($message)
|
5584 |
+
{
|
5585 |
+
$this->messages .= $message . "\n";
|
5586 |
+
}
|
5587 |
+
|
5588 |
+
/**
|
5589 |
+
* a few functions which should allow the document to be treated transactionally.
|
5590 |
+
*
|
5591 |
+
* @param $action
|
5592 |
+
*/
|
5593 |
+
function transaction($action)
|
5594 |
+
{
|
5595 |
+
switch ($action) {
|
5596 |
+
case 'start':
|
5597 |
+
// store all the data away into the checkpoint variable
|
5598 |
+
$data = get_object_vars($this);
|
5599 |
+
$this->checkpoint = $data;
|
5600 |
+
unset($data);
|
5601 |
+
break;
|
5602 |
+
|
5603 |
+
case 'commit':
|
5604 |
+
if (is_array($this->checkpoint) && isset($this->checkpoint['checkpoint'])) {
|
5605 |
+
$tmp = $this->checkpoint['checkpoint'];
|
5606 |
+
$this->checkpoint = $tmp;
|
5607 |
+
unset($tmp);
|
5608 |
+
} else {
|
5609 |
+
$this->checkpoint = '';
|
5610 |
+
}
|
5611 |
+
break;
|
5612 |
+
|
5613 |
+
case 'rewind':
|
5614 |
+
// do not destroy the current checkpoint, but move us back to the state then, so that we can try again
|
5615 |
+
if (is_array($this->checkpoint)) {
|
5616 |
+
// can only abort if were inside a checkpoint
|
5617 |
+
$tmp = $this->checkpoint;
|
5618 |
+
|
5619 |
+
foreach ($tmp as $k => $v) {
|
5620 |
+
if ($k !== 'checkpoint') {
|
5621 |
+
$this->$k = $v;
|
5622 |
+
}
|
5623 |
+
}
|
5624 |
+
unset($tmp);
|
5625 |
+
}
|
5626 |
+
break;
|
5627 |
+
|
5628 |
+
case 'abort':
|
5629 |
+
if (is_array($this->checkpoint)) {
|
5630 |
+
// can only abort if were inside a checkpoint
|
5631 |
+
$tmp = $this->checkpoint;
|
5632 |
+
foreach ($tmp as $k => $v) {
|
5633 |
+
$this->$k = $v;
|
5634 |
+
}
|
5635 |
+
unset($tmp);
|
5636 |
+
}
|
5637 |
+
break;
|
5638 |
+
}
|
5639 |
+
}
|
5640 |
+
}
|
vendor/dompdf/dompdf/lib/html5lib/Data.php
CHANGED
@@ -1,123 +1,123 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
// warning: this file is encoded in UTF-8!
|
4 |
-
|
5 |
-
class HTML5_Data
|
6 |
-
{
|
7 |
-
|
8 |
-
// at some point this should be moved to a .ser file. Another
|
9 |
-
// possible optimization is to give UTF-8 bytes, not Unicode
|
10 |
-
// codepoints
|
11 |
-
// XXX: Not quite sure why it's named this; this is
|
12 |
-
// actually the numeric entity dereference table.
|
13 |
-
protected static $realCodepointTable = array(
|
14 |
-
0x00 => 0xFFFD, // REPLACEMENT CHARACTER
|
15 |
-
0x0D => 0x000A, // LINE FEED (LF)
|
16 |
-
0x80 => 0x20AC, // EURO SIGN ('€')
|
17 |
-
0x81 => 0x0081, // <control>
|
18 |
-
0x82 => 0x201A, // SINGLE LOW-9 QUOTATION MARK ('‚')
|
19 |
-
0x83 => 0x0192, // LATIN SMALL LETTER F WITH HOOK ('ƒ')
|
20 |
-
0x84 => 0x201E, // DOUBLE LOW-9 QUOTATION MARK ('„')
|
21 |
-
0x85 => 0x2026, // HORIZONTAL ELLIPSIS ('…')
|
22 |
-
0x86 => 0x2020, // DAGGER ('†')
|
23 |
-
0x87 => 0x2021, // DOUBLE DAGGER ('‡')
|
24 |
-
0x88 => 0x02C6, // MODIFIER LETTER CIRCUMFLEX ACCENT ('ˆ')
|
25 |
-
0x89 => 0x2030, // PER MILLE SIGN ('‰')
|
26 |
-
0x8A => 0x0160, // LATIN CAPITAL LETTER S WITH CARON ('Š')
|
27 |
-
0x8B => 0x2039, // SINGLE LEFT-POINTING ANGLE QUOTATION MARK ('‹')
|
28 |
-
0x8C => 0x0152, // LATIN CAPITAL LIGATURE OE ('Œ')
|
29 |
-
0x8D => 0x008D, // <control>
|
30 |
-
0x8E => 0x017D, // LATIN CAPITAL LETTER Z WITH CARON ('Ž')
|
31 |
-
0x8F => 0x008F, // <control>
|
32 |
-
0x90 => 0x0090, // <control>
|
33 |
-
0x91 => 0x2018, // LEFT SINGLE QUOTATION MARK ('‘')
|
34 |
-
0x92 => 0x2019, // RIGHT SINGLE QUOTATION MARK ('’')
|
35 |
-
0x93 => 0x201C, // LEFT DOUBLE QUOTATION MARK ('“')
|
36 |
-
0x94 => 0x201D, // RIGHT DOUBLE QUOTATION MARK ('”')
|
37 |
-
0x95 => 0x2022, // BULLET ('•')
|
38 |
-
0x96 => 0x2013, // EN DASH ('–')
|
39 |
-
0x97 => 0x2014, // EM DASH ('—')
|
40 |
-
0x98 => 0x02DC, // SMALL TILDE ('˜')
|
41 |
-
0x99 => 0x2122, // TRADE MARK SIGN ('™')
|
42 |
-
0x9A => 0x0161, // LATIN SMALL LETTER S WITH CARON ('š')
|
43 |
-
0x9B => 0x203A, // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK ('›')
|
44 |
-
0x9C => 0x0153, // LATIN SMALL LIGATURE OE ('œ')
|
45 |
-
0x9D => 0x009D, // <control>
|
46 |
-
0x9E => 0x017E, // LATIN SMALL LETTER Z WITH CARON ('ž')
|
47 |
-
0x9F => 0x0178, // LATIN CAPITAL LETTER Y WITH DIAERESIS ('Ÿ')
|
48 |
-
);
|
49 |
-
|
50 |
-
protected static $namedCharacterReferences;
|
51 |
-
|
52 |
-
protected static $namedCharacterReferenceMaxLength;
|
53 |
-
|
54 |
-
/**
|
55 |
-
* Returns the "real" Unicode codepoint of a malformed character
|
56 |
-
* reference.
|
57 |
-
*/
|
58 |
-
public static function getRealCodepoint($ref) {
|
59 |
-
if (!isset(self::$realCodepointTable[$ref])) {
|
60 |
-
return false;
|
61 |
-
} else {
|
62 |
-
return self::$realCodepointTable[$ref];
|
63 |
-
}
|
64 |
-
}
|
65 |
-
|
66 |
-
public static function getNamedCharacterReferences() {
|
67 |
-
if (!self::$namedCharacterReferences) {
|
68 |
-
self::$namedCharacterReferences = unserialize(
|
69 |
-
file_get_contents(dirname(__FILE__) . '/named-character-references.ser'));
|
70 |
-
}
|
71 |
-
return self::$namedCharacterReferences;
|
72 |
-
}
|
73 |
-
|
74 |
-
/**
|
75 |
-
* Converts a Unicode codepoint to sequence of UTF-8 bytes.
|
76 |
-
* @note Shamelessly stolen from HTML Purifier, which is also
|
77 |
-
* shamelessly stolen from Feyd (which is in public domain).
|
78 |
-
*/
|
79 |
-
public static function utf8chr($code) {
|
80 |
-
/* We don't care: we live dangerously
|
81 |
-
* if($code > 0x10FFFF or $code < 0x0 or
|
82 |
-
($code >= 0xD800 and $code <= 0xDFFF) ) {
|
83 |
-
// bits are set outside the "valid" range as defined
|
84 |
-
// by UNICODE 4.1.0
|
85 |
-
return "\xEF\xBF\xBD";
|
86 |
-
}*/
|
87 |
-
|
88 |
-
$y = $z = $w = 0;
|
89 |
-
if ($code < 0x80) {
|
90 |
-
// regular ASCII character
|
91 |
-
$x = $code;
|
92 |
-
} else {
|
93 |
-
// set up bits for UTF-8
|
94 |
-
$x = ($code & 0x3F) | 0x80;
|
95 |
-
if ($code < 0x800) {
|
96 |
-
$y = (($code & 0x7FF) >> 6) | 0xC0;
|
97 |
-
} else {
|
98 |
-
$y = (($code & 0xFC0) >> 6) | 0x80;
|
99 |
-
if ($code < 0x10000) {
|
100 |
-
$z = (($code >> 12) & 0x0F) | 0xE0;
|
101 |
-
} else {
|
102 |
-
$z = (($code >> 12) & 0x3F) | 0x80;
|
103 |
-
$w = (($code >> 18) & 0x07) | 0xF0;
|
104 |
-
}
|
105 |
-
}
|
106 |
-
}
|
107 |
-
// set up the actual character
|
108 |
-
$ret = '';
|
109 |
-
if ($w) {
|
110 |
-
$ret .= chr($w);
|
111 |
-
}
|
112 |
-
if ($z) {
|
113 |
-
$ret .= chr($z);
|
114 |
-
}
|
115 |
-
if ($y) {
|
116 |
-
$ret .= chr($y);
|
117 |
-
}
|
118 |
-
$ret .= chr($x);
|
119 |
-
|
120 |
-
return $ret;
|
121 |
-
}
|
122 |
-
|
123 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// warning: this file is encoded in UTF-8!
|
4 |
+
|
5 |
+
class HTML5_Data
|
6 |
+
{
|
7 |
+
|
8 |
+
// at some point this should be moved to a .ser file. Another
|
9 |
+
// possible optimization is to give UTF-8 bytes, not Unicode
|
10 |
+
// codepoints
|
11 |
+
// XXX: Not quite sure why it's named this; this is
|
12 |
+
// actually the numeric entity dereference table.
|
13 |
+
protected static $realCodepointTable = array(
|
14 |
+
0x00 => 0xFFFD, // REPLACEMENT CHARACTER
|
15 |
+
0x0D => 0x000A, // LINE FEED (LF)
|
16 |
+
0x80 => 0x20AC, // EURO SIGN ('€')
|
17 |
+
0x81 => 0x0081, // <control>
|
18 |
+
0x82 => 0x201A, // SINGLE LOW-9 QUOTATION MARK ('‚')
|
19 |
+
0x83 => 0x0192, // LATIN SMALL LETTER F WITH HOOK ('ƒ')
|
20 |
+
0x84 => 0x201E, // DOUBLE LOW-9 QUOTATION MARK ('„')
|
21 |
+
0x85 => 0x2026, // HORIZONTAL ELLIPSIS ('…')
|
22 |
+
0x86 => 0x2020, // DAGGER ('†')
|
23 |
+
0x87 => 0x2021, // DOUBLE DAGGER ('‡')
|
24 |
+
0x88 => 0x02C6, // MODIFIER LETTER CIRCUMFLEX ACCENT ('ˆ')
|
25 |
+
0x89 => 0x2030, // PER MILLE SIGN ('‰')
|
26 |
+
0x8A => 0x0160, // LATIN CAPITAL LETTER S WITH CARON ('Š')
|
27 |
+
0x8B => 0x2039, // SINGLE LEFT-POINTING ANGLE QUOTATION MARK ('‹')
|
28 |
+
0x8C => 0x0152, // LATIN CAPITAL LIGATURE OE ('Œ')
|
29 |
+
0x8D => 0x008D, // <control>
|
30 |
+
0x8E => 0x017D, // LATIN CAPITAL LETTER Z WITH CARON ('Ž')
|
31 |
+
0x8F => 0x008F, // <control>
|
32 |
+
0x90 => 0x0090, // <control>
|
33 |
+
0x91 => 0x2018, // LEFT SINGLE QUOTATION MARK ('‘')
|
34 |
+
0x92 => 0x2019, // RIGHT SINGLE QUOTATION MARK ('’')
|
35 |
+
0x93 => 0x201C, // LEFT DOUBLE QUOTATION MARK ('“')
|
36 |
+
0x94 => 0x201D, // RIGHT DOUBLE QUOTATION MARK ('”')
|
37 |
+
0x95 => 0x2022, // BULLET ('•')
|
38 |
+
0x96 => 0x2013, // EN DASH ('–')
|
39 |
+
0x97 => 0x2014, // EM DASH ('—')
|
40 |
+
0x98 => 0x02DC, // SMALL TILDE ('˜')
|
41 |
+
0x99 => 0x2122, // TRADE MARK SIGN ('™')
|
42 |
+
0x9A => 0x0161, // LATIN SMALL LETTER S WITH CARON ('š')
|
43 |
+
0x9B => 0x203A, // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK ('›')
|
44 |
+
0x9C => 0x0153, // LATIN SMALL LIGATURE OE ('œ')
|
45 |
+
0x9D => 0x009D, // <control>
|
46 |
+
0x9E => 0x017E, // LATIN SMALL LETTER Z WITH CARON ('ž')
|
47 |
+
0x9F => 0x0178, // LATIN CAPITAL LETTER Y WITH DIAERESIS ('Ÿ')
|
48 |
+
);
|
49 |
+
|
50 |
+
protected static $namedCharacterReferences;
|
51 |
+
|
52 |
+
protected static $namedCharacterReferenceMaxLength;
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Returns the "real" Unicode codepoint of a malformed character
|
56 |
+
* reference.
|
57 |
+
*/
|
58 |
+
public static function getRealCodepoint($ref) {
|
59 |
+
if (!isset(self::$realCodepointTable[$ref])) {
|
60 |
+
return false;
|
61 |
+
} else {
|
62 |
+
return self::$realCodepointTable[$ref];
|
63 |
+
}
|
64 |
+
}
|
65 |
+
|
66 |
+
public static function getNamedCharacterReferences() {
|
67 |
+
if (!self::$namedCharacterReferences) {
|
68 |
+
self::$namedCharacterReferences = unserialize(
|
69 |
+
file_get_contents(dirname(__FILE__) . '/named-character-references.ser'));
|
70 |
+
}
|
71 |
+
return self::$namedCharacterReferences;
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Converts a Unicode codepoint to sequence of UTF-8 bytes.
|
76 |
+
* @note Shamelessly stolen from HTML Purifier, which is also
|
77 |
+
* shamelessly stolen from Feyd (which is in public domain).
|
78 |
+
*/
|
79 |
+
public static function utf8chr($code) {
|
80 |
+
/* We don't care: we live dangerously
|
81 |
+
* if($code > 0x10FFFF or $code < 0x0 or
|
82 |
+
($code >= 0xD800 and $code <= 0xDFFF) ) {
|
83 |
+
// bits are set outside the "valid" range as defined
|
84 |
+
// by UNICODE 4.1.0
|
85 |
+
return "\xEF\xBF\xBD";
|
86 |
+
}*/
|
87 |
+
|
88 |
+
$y = $z = $w = 0;
|
89 |
+
if ($code < 0x80) {
|
90 |
+
// regular ASCII character
|
91 |
+
$x = $code;
|
92 |
+
} else {
|
93 |
+
// set up bits for UTF-8
|
94 |
+
$x = ($code & 0x3F) | 0x80;
|
95 |
+
if ($code < 0x800) {
|
96 |
+
$y = (($code & 0x7FF) >> 6) | 0xC0;
|
97 |
+
} else {
|
98 |
+
$y = (($code & 0xFC0) >> 6) | 0x80;
|
99 |
+
if ($code < 0x10000) {
|
100 |
+
$z = (($code >> 12) & 0x0F) | 0xE0;
|
101 |
+
} else {
|
102 |
+
$z = (($code >> 12) & 0x3F) | 0x80;
|
103 |
+
$w = (($code >> 18) & 0x07) | 0xF0;
|
104 |
+
}
|
105 |
+
}
|
106 |
+
}
|
107 |
+
// set up the actual character
|
108 |
+
$ret = '';
|
109 |
+
if ($w) {
|
110 |
+
$ret .= chr($w);
|
111 |
+
}
|
112 |
+
if ($z) {
|
113 |
+
$ret .= chr($z);
|
114 |
+
}
|
115 |
+
if ($y) {
|
116 |
+
$ret .= chr($y);
|
117 |
+
}
|
118 |
+
$ret .= chr($x);
|
119 |
+
|
120 |
+
return $ret;
|
121 |
+
}
|
122 |
+
|
123 |
+
}
|
vendor/dompdf/dompdf/lib/html5lib/Tokenizer.php
CHANGED
@@ -1,2470 +1,2470 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/*
|
4 |
-
|
5 |
-
Copyright 2007 Jeroen van der Meer <http://jero.net/>
|
6 |
-
Copyright 2008 Edward Z. Yang <http://htmlpurifier.org/>
|
7 |
-
Copyright 2009 Geoffrey Sneddon <http://gsnedders.com/>
|
8 |
-
|
9 |
-
Permission is hereby granted, free of charge, to any person obtaining a
|
10 |
-
copy of this software and associated documentation files (the
|
11 |
-
"Software"), to deal in the Software without restriction, including
|
12 |
-
without limitation the rights to use, copy, modify, merge, publish,
|
13 |
-
distribute, sublicense, and/or sell copies of the Software, and to
|
14 |
-
permit persons to whom the Software is furnished to do so, subject to
|
15 |
-
the following conditions:
|
16 |
-
|
17 |
-
The above copyright notice and this permission notice shall be included
|
18 |
-
in all copies or substantial portions of the Software.
|
19 |
-
|
20 |
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
21 |
-
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22 |
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
23 |
-
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
24 |
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
25 |
-
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
26 |
-
SOFTWARE OR THE USE OR
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|