1 | <?php |
---|
2 | /** |
---|
3 | * Cpdf |
---|
4 | * |
---|
5 | * http://www.ros.co.nz/pdf |
---|
6 | * |
---|
7 | * A PHP class to provide the basic functionality to create a pdf document without |
---|
8 | * any requirement for additional modules. |
---|
9 | * |
---|
10 | * Note that they companion class CezPdf can be used to extend this class and dramatically |
---|
11 | * simplify the creation of documents. |
---|
12 | * |
---|
13 | * IMPORTANT NOTE |
---|
14 | * there is no warranty, implied or otherwise with this software. |
---|
15 | * |
---|
16 | * LICENCE |
---|
17 | * This code has been placed in the Public Domain for all to enjoy. |
---|
18 | * |
---|
19 | * @author Wayne Munro <pdf@ros.co.nz> |
---|
20 | * @version 009 |
---|
21 | * @package Cpdf |
---|
22 | */ |
---|
23 | class Cpdf { |
---|
24 | |
---|
25 | |
---|
26 | /** |
---|
27 | * the current number of pdf objects in the document |
---|
28 | */ |
---|
29 | public $numObj = 0; |
---|
30 | |
---|
31 | /** |
---|
32 | * this array contains all of the pdf objects, ready for final assembly |
---|
33 | */ |
---|
34 | public $objects = array(); |
---|
35 | |
---|
36 | /** |
---|
37 | * the objectId (number within the objects array) of the document catalog |
---|
38 | */ |
---|
39 | public $catalogId; |
---|
40 | |
---|
41 | /** |
---|
42 | * array carrying information about the fonts that the system currently knows about |
---|
43 | * used to ensure that a font is not loaded twice, among other things |
---|
44 | */ |
---|
45 | public $fonts = array(); |
---|
46 | |
---|
47 | /** |
---|
48 | * a record of the current font |
---|
49 | */ |
---|
50 | public $currentFont = ''; |
---|
51 | |
---|
52 | /** |
---|
53 | * the current base font |
---|
54 | */ |
---|
55 | public $currentBaseFont = ''; |
---|
56 | |
---|
57 | /** |
---|
58 | * the number of the current font within the font array |
---|
59 | */ |
---|
60 | public $currentFontNum = 0; |
---|
61 | |
---|
62 | /** |
---|
63 | * |
---|
64 | */ |
---|
65 | public $currentNode; |
---|
66 | |
---|
67 | /** |
---|
68 | * object number of the current page |
---|
69 | */ |
---|
70 | public $currentPage; |
---|
71 | |
---|
72 | /** |
---|
73 | * object number of the currently active contents block |
---|
74 | */ |
---|
75 | public $currentContents; |
---|
76 | |
---|
77 | /** |
---|
78 | * number of fonts within the system |
---|
79 | */ |
---|
80 | public $numFonts = 0; |
---|
81 | |
---|
82 | /** |
---|
83 | * Number of graphic state resources used |
---|
84 | */ |
---|
85 | private $numStates = 0; |
---|
86 | |
---|
87 | |
---|
88 | /** |
---|
89 | * current colour for fill operations, defaults to inactive value, all three components should be between 0 and 1 inclusive when active |
---|
90 | */ |
---|
91 | public $currentColour = array('r'=>-1, 'g'=>-1, 'b'=>-1); |
---|
92 | |
---|
93 | /** |
---|
94 | * current colour for stroke operations (lines etc.) |
---|
95 | */ |
---|
96 | public $currentStrokeColour = array('r'=>-1, 'g'=>-1, 'b'=>-1); |
---|
97 | |
---|
98 | /** |
---|
99 | * current style that lines are drawn in |
---|
100 | */ |
---|
101 | public $currentLineStyle = ''; |
---|
102 | |
---|
103 | /** |
---|
104 | * current line transparency (partial graphics state) |
---|
105 | */ |
---|
106 | public $currentLineTransparency = array("mode" => "Normal", "opacity" => 1.0); |
---|
107 | |
---|
108 | /** |
---|
109 | * current fill transparency (partial graphics state) |
---|
110 | */ |
---|
111 | public $currentFillTransparency = array("mode" => "Normal", "opacity" => 1.0); |
---|
112 | |
---|
113 | /** |
---|
114 | * an array which is used to save the state of the document, mainly the colours and styles |
---|
115 | * it is used to temporarily change to another state, the change back to what it was before |
---|
116 | */ |
---|
117 | public $stateStack = array(); |
---|
118 | |
---|
119 | /** |
---|
120 | * number of elements within the state stack |
---|
121 | */ |
---|
122 | public $nStateStack = 0; |
---|
123 | |
---|
124 | /** |
---|
125 | * number of page objects within the document |
---|
126 | */ |
---|
127 | public $numPages = 0; |
---|
128 | |
---|
129 | /** |
---|
130 | * object Id storage stack |
---|
131 | */ |
---|
132 | public $stack = array(); |
---|
133 | |
---|
134 | /** |
---|
135 | * number of elements within the object Id storage stack |
---|
136 | */ |
---|
137 | public $nStack = 0; |
---|
138 | |
---|
139 | /** |
---|
140 | * an array which contains information about the objects which are not firmly attached to pages |
---|
141 | * these have been added with the addObject function |
---|
142 | */ |
---|
143 | public $looseObjects = array(); |
---|
144 | |
---|
145 | /** |
---|
146 | * array contains infomation about how the loose objects are to be added to the document |
---|
147 | */ |
---|
148 | public $addLooseObjects = array(); |
---|
149 | |
---|
150 | /** |
---|
151 | * the objectId of the information object for the document |
---|
152 | * this contains authorship, title etc. |
---|
153 | */ |
---|
154 | public $infoObject = 0; |
---|
155 | |
---|
156 | /** |
---|
157 | * number of images being tracked within the document |
---|
158 | */ |
---|
159 | public $numImages = 0; |
---|
160 | |
---|
161 | /** |
---|
162 | * an array containing options about the document |
---|
163 | * it defaults to turning on the compression of the objects |
---|
164 | */ |
---|
165 | public $options = array('compression'=>1); |
---|
166 | |
---|
167 | /** |
---|
168 | * the objectId of the first page of the document |
---|
169 | */ |
---|
170 | public $firstPageId; |
---|
171 | |
---|
172 | /** |
---|
173 | * used to track the last used value of the inter-word spacing, this is so that it is known |
---|
174 | * when the spacing is changed. |
---|
175 | */ |
---|
176 | public $wordSpaceAdjust = 0; |
---|
177 | |
---|
178 | /** |
---|
179 | * the object Id of the procset object |
---|
180 | */ |
---|
181 | public $procsetObjectId; |
---|
182 | |
---|
183 | /** |
---|
184 | * store the information about the relationship between font families |
---|
185 | * this used so that the code knows which font is the bold version of another font, etc. |
---|
186 | * the value of this array is initialised in the constuctor function. |
---|
187 | */ |
---|
188 | public $fontFamilies = array(); |
---|
189 | |
---|
190 | /** |
---|
191 | * track if the current font is bolded or italicised |
---|
192 | */ |
---|
193 | public $currentTextState = ''; |
---|
194 | |
---|
195 | /** |
---|
196 | * messages are stored here during processing, these can be selected afterwards to give some useful debug information |
---|
197 | */ |
---|
198 | public $messages = ''; |
---|
199 | |
---|
200 | /** |
---|
201 | * the ancryption array for the document encryption is stored here |
---|
202 | */ |
---|
203 | public $arc4 = ''; |
---|
204 | |
---|
205 | /** |
---|
206 | * the object Id of the encryption information |
---|
207 | */ |
---|
208 | public $arc4_objnum = 0; |
---|
209 | |
---|
210 | /** |
---|
211 | * the file identifier, used to uniquely identify a pdf document |
---|
212 | */ |
---|
213 | public $fileIdentifier = ''; |
---|
214 | |
---|
215 | /** |
---|
216 | * a flag to say if a document is to be encrypted or not |
---|
217 | */ |
---|
218 | public $encrypted = 0; |
---|
219 | |
---|
220 | /** |
---|
221 | * the ancryption key for the encryption of all the document content (structure is not encrypted) |
---|
222 | */ |
---|
223 | public $encryptionKey = ''; |
---|
224 | |
---|
225 | /** |
---|
226 | * array which forms a stack to keep track of nested callback functions |
---|
227 | */ |
---|
228 | public $callback = array(); |
---|
229 | |
---|
230 | /** |
---|
231 | * the number of callback functions in the callback array |
---|
232 | */ |
---|
233 | public $nCallback = 0; |
---|
234 | |
---|
235 | /** |
---|
236 | * store label->id pairs for named destinations, these will be used to replace internal links |
---|
237 | * done this way so that destinations can be defined after the location that links to them |
---|
238 | */ |
---|
239 | public $destinations = array(); |
---|
240 | |
---|
241 | /** |
---|
242 | * store the stack for the transaction commands, each item in here is a record of the values of all the |
---|
243 | * publiciables within the class, so that the user can rollback at will (from each 'start' command) |
---|
244 | * note that this includes the objects array, so these can be large. |
---|
245 | */ |
---|
246 | public $checkpoint = ''; |
---|
247 | |
---|
248 | /** |
---|
249 | * class constructor |
---|
250 | * this will start a new document |
---|
251 | * @var array array of 4 numbers, defining the bottom left and upper right corner of the page. first two are normally zero. |
---|
252 | */ |
---|
253 | function Cpdf ($pageSize = array(0, 0, 612, 792)) { |
---|
254 | |
---|
255 | $this->newDocument($pageSize); |
---|
256 | |
---|
257 | |
---|
258 | // also initialize the font families that are known about already |
---|
259 | $this->setFontFamily('init'); |
---|
260 | |
---|
261 | // $this->fileIdentifier = md5('xxxxxxxx'.time()); |
---|
262 | |
---|
263 | |
---|
264 | } |
---|
265 | |
---|
266 | |
---|
267 | /** |
---|
268 | * Document object methods (internal use only) |
---|
269 | * |
---|
270 | * There is about one object method for each type of object in the pdf document |
---|
271 | * Each function has the same call list ($id,$action,$options). |
---|
272 | * $id = the object ID of the object, or what it is to be if it is being created |
---|
273 | * $action = a string specifying the action to be performed, though ALL must support: |
---|
274 | * 'new' - create the object with the id $id |
---|
275 | * 'out' - produce the output for the pdf object |
---|
276 | * $options = optional, a string or array containing the various parameters for the object |
---|
277 | * |
---|
278 | * These, in conjunction with the output function are the ONLY way for output to be produced |
---|
279 | * within the pdf 'file'. |
---|
280 | */ |
---|
281 | |
---|
282 | /** |
---|
283 | *destination object, used to specify the location for the user to jump to, presently on opening |
---|
284 | */ |
---|
285 | function o_destination($id, $action, $options = '') { |
---|
286 | |
---|
287 | if ($action != 'new') { |
---|
288 | |
---|
289 | $o = & $this->objects[$id]; |
---|
290 | } |
---|
291 | |
---|
292 | switch ($action) { |
---|
293 | |
---|
294 | case 'new': |
---|
295 | |
---|
296 | $this->objects[$id] = array('t'=>'destination', 'info'=>array()); |
---|
297 | |
---|
298 | $tmp = ''; |
---|
299 | |
---|
300 | switch ($options['type']) { |
---|
301 | |
---|
302 | case 'XYZ': |
---|
303 | |
---|
304 | case 'FitR': |
---|
305 | |
---|
306 | $tmp = ' '.$options['p3'].$tmp; |
---|
307 | |
---|
308 | case 'FitH': |
---|
309 | |
---|
310 | case 'FitV': |
---|
311 | |
---|
312 | case 'FitBH': |
---|
313 | |
---|
314 | case 'FitBV': |
---|
315 | |
---|
316 | $tmp = ' '.$options['p1'].' '.$options['p2'].$tmp; |
---|
317 | |
---|
318 | case 'Fit': |
---|
319 | |
---|
320 | case 'FitB': |
---|
321 | |
---|
322 | $tmp = $options['type'].$tmp; |
---|
323 | |
---|
324 | $this->objects[$id]['info']['string'] = $tmp; |
---|
325 | |
---|
326 | $this->objects[$id]['info']['page'] = $options['page']; |
---|
327 | } |
---|
328 | |
---|
329 | break; |
---|
330 | |
---|
331 | case 'out': |
---|
332 | |
---|
333 | $tmp = $o['info']; |
---|
334 | |
---|
335 | $res = "\n".$id." 0 obj\n".'['.$tmp['page'].' 0 R /'.$tmp['string']."]\nendobj\n"; |
---|
336 | |
---|
337 | return $res; |
---|
338 | |
---|
339 | break; |
---|
340 | } |
---|
341 | } |
---|
342 | |
---|
343 | |
---|
344 | /** |
---|
345 | * set the viewer preferences |
---|
346 | */ |
---|
347 | function o_viewerPreferences($id, $action, $options = '') { |
---|
348 | |
---|
349 | if ($action != 'new') { |
---|
350 | |
---|
351 | $o = & $this->objects[$id]; |
---|
352 | } |
---|
353 | |
---|
354 | switch ($action) { |
---|
355 | |
---|
356 | case 'new': |
---|
357 | |
---|
358 | $this->objects[$id] = array('t'=>'viewerPreferences', 'info'=>array()); |
---|
359 | |
---|
360 | break; |
---|
361 | |
---|
362 | case 'add': |
---|
363 | |
---|
364 | foreach($options as $k=>$v) { |
---|
365 | |
---|
366 | switch ($k) { |
---|
367 | |
---|
368 | case 'HideToolbar': |
---|
369 | |
---|
370 | case 'HideMenubar': |
---|
371 | |
---|
372 | case 'HideWindowUI': |
---|
373 | |
---|
374 | case 'FitWindow': |
---|
375 | |
---|
376 | case 'CenterWindow': |
---|
377 | |
---|
378 | case 'NonFullScreenPageMode': |
---|
379 | |
---|
380 | case 'Direction': |
---|
381 | |
---|
382 | $o['info'][$k] = $v; |
---|
383 | |
---|
384 | break; |
---|
385 | } |
---|
386 | } |
---|
387 | |
---|
388 | break; |
---|
389 | |
---|
390 | case 'out': |
---|
391 | |
---|
392 | |
---|
393 | $res = "\n".$id." 0 obj\n".'<< '; |
---|
394 | |
---|
395 | foreach($o['info'] as $k=>$v) { |
---|
396 | |
---|
397 | $res.= "\n/".$k.' '.$v; |
---|
398 | } |
---|
399 | |
---|
400 | $res.= "\n>>\n"; |
---|
401 | |
---|
402 | return $res; |
---|
403 | |
---|
404 | break; |
---|
405 | } |
---|
406 | } |
---|
407 | |
---|
408 | |
---|
409 | /** |
---|
410 | * define the document catalog, the overall controller for the document |
---|
411 | */ |
---|
412 | function o_catalog($id, $action, $options = '') { |
---|
413 | |
---|
414 | if ($action != 'new') { |
---|
415 | |
---|
416 | $o = & $this->objects[$id]; |
---|
417 | } |
---|
418 | |
---|
419 | switch ($action) { |
---|
420 | |
---|
421 | case 'new': |
---|
422 | |
---|
423 | $this->objects[$id] = array('t'=>'catalog', 'info'=>array()); |
---|
424 | |
---|
425 | $this->catalogId = $id; |
---|
426 | |
---|
427 | break; |
---|
428 | |
---|
429 | case 'outlines': |
---|
430 | |
---|
431 | case 'pages': |
---|
432 | |
---|
433 | case 'openHere': |
---|
434 | |
---|
435 | $o['info'][$action] = $options; |
---|
436 | |
---|
437 | break; |
---|
438 | |
---|
439 | case 'viewerPreferences': |
---|
440 | |
---|
441 | if (!isset($o['info']['viewerPreferences'])) { |
---|
442 | |
---|
443 | $this->numObj++; |
---|
444 | |
---|
445 | $this->o_viewerPreferences($this->numObj, 'new'); |
---|
446 | |
---|
447 | $o['info']['viewerPreferences'] = $this->numObj; |
---|
448 | } |
---|
449 | |
---|
450 | $vp = $o['info']['viewerPreferences']; |
---|
451 | |
---|
452 | $this->o_viewerPreferences($vp, 'add', $options); |
---|
453 | |
---|
454 | break; |
---|
455 | |
---|
456 | case 'out': |
---|
457 | |
---|
458 | $res = "\n".$id." 0 obj\n".'<< /Type /Catalog'; |
---|
459 | |
---|
460 | foreach($o['info'] as $k=>$v) { |
---|
461 | |
---|
462 | switch ($k) { |
---|
463 | |
---|
464 | case 'outlines': |
---|
465 | |
---|
466 | $res.= "\n".'/Outlines '.$v.' 0 R'; |
---|
467 | |
---|
468 | break; |
---|
469 | |
---|
470 | case 'pages': |
---|
471 | |
---|
472 | $res.= "\n".'/Pages '.$v.' 0 R'; |
---|
473 | |
---|
474 | break; |
---|
475 | |
---|
476 | case 'viewerPreferences': |
---|
477 | |
---|
478 | $res.= "\n".'/ViewerPreferences '.$o['info']['viewerPreferences'].' 0 R'; |
---|
479 | |
---|
480 | break; |
---|
481 | |
---|
482 | case 'openHere': |
---|
483 | |
---|
484 | $res.= "\n".'/OpenAction '.$o['info']['openHere'].' 0 R'; |
---|
485 | |
---|
486 | break; |
---|
487 | } |
---|
488 | } |
---|
489 | |
---|
490 | $res.= " >>\nendobj"; |
---|
491 | |
---|
492 | return $res; |
---|
493 | |
---|
494 | break; |
---|
495 | } |
---|
496 | } |
---|
497 | |
---|
498 | |
---|
499 | /** |
---|
500 | * object which is a parent to the pages in the document |
---|
501 | */ |
---|
502 | function o_pages($id, $action, $options = '') { |
---|
503 | |
---|
504 | if ($action != 'new') { |
---|
505 | |
---|
506 | $o = & $this->objects[$id]; |
---|
507 | } |
---|
508 | |
---|
509 | switch ($action) { |
---|
510 | |
---|
511 | case 'new': |
---|
512 | |
---|
513 | $this->objects[$id] = array('t'=>'pages', 'info'=>array()); |
---|
514 | |
---|
515 | $this->o_catalog($this->catalogId, 'pages', $id); |
---|
516 | |
---|
517 | break; |
---|
518 | |
---|
519 | case 'page': |
---|
520 | |
---|
521 | if (!is_array($options)) { |
---|
522 | |
---|
523 | // then it will just be the id of the new page |
---|
524 | $o['info']['pages'][] = $options; |
---|
525 | } else { |
---|
526 | |
---|
527 | // then it should be an array having 'id','rid','pos', where rid=the page to which this one will be placed relative |
---|
528 | // and pos is either 'before' or 'after', saying where this page will fit. |
---|
529 | if (isset($options['id']) && isset($options['rid']) && isset($options['pos'])) { |
---|
530 | |
---|
531 | $i = array_search($options['rid'], $o['info']['pages']); |
---|
532 | |
---|
533 | if (isset($o['info']['pages'][$i]) && $o['info']['pages'][$i] == $options['rid']) { |
---|
534 | |
---|
535 | // then there is a match |
---|
536 | // make a space |
---|
537 | switch ($options['pos']) { |
---|
538 | |
---|
539 | case 'before': |
---|
540 | |
---|
541 | $k = $i; |
---|
542 | |
---|
543 | break; |
---|
544 | |
---|
545 | case 'after': |
---|
546 | |
---|
547 | $k = $i+1; |
---|
548 | |
---|
549 | break; |
---|
550 | |
---|
551 | default: |
---|
552 | |
---|
553 | $k = -1; |
---|
554 | |
---|
555 | break; |
---|
556 | } |
---|
557 | |
---|
558 | if ($k >= 0) { |
---|
559 | |
---|
560 | for ($j = count($o['info']['pages']) -1;$j >= $k;$j--) { |
---|
561 | |
---|
562 | $o['info']['pages'][$j+1] = $o['info']['pages'][$j]; |
---|
563 | } |
---|
564 | |
---|
565 | $o['info']['pages'][$k] = $options['id']; |
---|
566 | } |
---|
567 | } |
---|
568 | } |
---|
569 | } |
---|
570 | |
---|
571 | break; |
---|
572 | |
---|
573 | case 'procset': |
---|
574 | |
---|
575 | $o['info']['procset'] = $options; |
---|
576 | |
---|
577 | break; |
---|
578 | |
---|
579 | case 'mediaBox': |
---|
580 | |
---|
581 | $o['info']['mediaBox'] = $options; |
---|
582 | // which should be an array of 4 numbers |
---|
583 | break; |
---|
584 | |
---|
585 | case 'font': |
---|
586 | |
---|
587 | $o['info']['fonts'][] = array('objNum'=>$options['objNum'], 'fontNum'=>$options['fontNum']); |
---|
588 | |
---|
589 | break; |
---|
590 | |
---|
591 | |
---|
592 | case 'extGState': |
---|
593 | |
---|
594 | $o['info']['extGStates'][] = array('objNum' => $options['objNum'], 'stateNum' => $options['stateNum']); |
---|
595 | |
---|
596 | break; |
---|
597 | |
---|
598 | |
---|
599 | case 'xObject': |
---|
600 | |
---|
601 | $o['info']['xObjects'][] = array('objNum'=>$options['objNum'], 'label'=>$options['label']); |
---|
602 | |
---|
603 | break; |
---|
604 | |
---|
605 | case 'out': |
---|
606 | |
---|
607 | if (count($o['info']['pages'])) { |
---|
608 | |
---|
609 | $res = "\n".$id." 0 obj\n<< /Type /Pages\n/Kids ["; |
---|
610 | |
---|
611 | foreach($o['info']['pages'] as $k=>$v) { |
---|
612 | |
---|
613 | $res.= $v." 0 R\n"; |
---|
614 | } |
---|
615 | |
---|
616 | $res.= "]\n/Count ".count($this->objects[$id]['info']['pages']); |
---|
617 | |
---|
618 | |
---|
619 | if ( (isset($o['info']['fonts']) && count($o['info']['fonts'])) || |
---|
620 | isset($o['info']['procset']) || |
---|
621 | (isset($o['info']['extGStates']) && count($o['info']['extGStates']))) { |
---|
622 | |
---|
623 | |
---|
624 | $res.= "\n/Resources <<"; |
---|
625 | |
---|
626 | if (isset($o['info']['procset'])) { |
---|
627 | |
---|
628 | $res.= "\n/ProcSet ".$o['info']['procset']." 0 R"; |
---|
629 | } |
---|
630 | |
---|
631 | if (isset($o['info']['fonts']) && count($o['info']['fonts'])) { |
---|
632 | |
---|
633 | $res.= "\n/Font << "; |
---|
634 | |
---|
635 | foreach($o['info']['fonts'] as $finfo) { |
---|
636 | |
---|
637 | $res.= "\n/F".$finfo['fontNum']." ".$finfo['objNum']." 0 R"; |
---|
638 | } |
---|
639 | |
---|
640 | $res.= " >>"; |
---|
641 | } |
---|
642 | |
---|
643 | if (isset($o['info']['xObjects']) && count($o['info']['xObjects'])) { |
---|
644 | |
---|
645 | $res.= "\n/XObject << "; |
---|
646 | |
---|
647 | foreach($o['info']['xObjects'] as $finfo) { |
---|
648 | |
---|
649 | $res.= "\n/".$finfo['label']." ".$finfo['objNum']." 0 R"; |
---|
650 | } |
---|
651 | |
---|
652 | $res.= " >>"; |
---|
653 | } |
---|
654 | |
---|
655 | if ( isset($o['info']['extGStates']) && count($o['info']['extGStates'])) { |
---|
656 | |
---|
657 | $res.= "\n/ExtGState << "; |
---|
658 | |
---|
659 | foreach ($o['info']['extGStates'] as $gstate) { |
---|
660 | |
---|
661 | $res.= "\n/GS" . $gstate['stateNum'] . " " . $gstate['objNum'] . " 0 R"; |
---|
662 | } |
---|
663 | |
---|
664 | $res.= " >>"; |
---|
665 | } |
---|
666 | |
---|
667 | |
---|
668 | $res.= "\n>>"; |
---|
669 | |
---|
670 | if (isset($o['info']['mediaBox'])) { |
---|
671 | |
---|
672 | $tmp = $o['info']['mediaBox']; |
---|
673 | |
---|
674 | $res.= "\n/MediaBox [".sprintf('%.3f', $tmp[0]) .' '.sprintf('%.3f', $tmp[1]) .' '.sprintf('%.3f', $tmp[2]) .' '.sprintf('%.3f', $tmp[3]) .']'; |
---|
675 | } |
---|
676 | } |
---|
677 | |
---|
678 | $res.= "\n >>\nendobj"; |
---|
679 | } else { |
---|
680 | |
---|
681 | $res = "\n".$id." 0 obj\n<< /Type /Pages\n/Count 0\n>>\nendobj"; |
---|
682 | } |
---|
683 | |
---|
684 | return $res; |
---|
685 | |
---|
686 | break; |
---|
687 | } |
---|
688 | } |
---|
689 | |
---|
690 | |
---|
691 | /** |
---|
692 | * define the outlines in the doc, empty for now |
---|
693 | */ |
---|
694 | function o_outlines($id, $action, $options = '') { |
---|
695 | |
---|
696 | if ($action != 'new') { |
---|
697 | |
---|
698 | $o = & $this->objects[$id]; |
---|
699 | } |
---|
700 | |
---|
701 | switch ($action) { |
---|
702 | |
---|
703 | case 'new': |
---|
704 | |
---|
705 | $this->objects[$id] = array('t'=>'outlines', 'info'=>array('outlines'=>array())); |
---|
706 | |
---|
707 | $this->o_catalog($this->catalogId, 'outlines', $id); |
---|
708 | |
---|
709 | break; |
---|
710 | |
---|
711 | case 'outline': |
---|
712 | |
---|
713 | $o['info']['outlines'][] = $options; |
---|
714 | |
---|
715 | break; |
---|
716 | |
---|
717 | case 'out': |
---|
718 | |
---|
719 | if (count($o['info']['outlines'])) { |
---|
720 | |
---|
721 | $res = "\n".$id." 0 obj\n<< /Type /Outlines /Kids ["; |
---|
722 | |
---|
723 | foreach($o['info']['outlines'] as $k=>$v) { |
---|
724 | |
---|
725 | $res.= $v." 0 R "; |
---|
726 | } |
---|
727 | |
---|
728 | $res.= "] /Count ".count($o['info']['outlines']) ." >>\nendobj"; |
---|
729 | } else { |
---|
730 | |
---|
731 | $res = "\n".$id." 0 obj\n<< /Type /Outlines /Count 0 >>\nendobj"; |
---|
732 | } |
---|
733 | |
---|
734 | return $res; |
---|
735 | |
---|
736 | break; |
---|
737 | } |
---|
738 | } |
---|
739 | |
---|
740 | |
---|
741 | /** |
---|
742 | * an object to hold the font description |
---|
743 | */ |
---|
744 | function o_font($id, $action, $options = '') { |
---|
745 | |
---|
746 | if ($action != 'new') { |
---|
747 | |
---|
748 | $o = & $this->objects[$id]; |
---|
749 | } |
---|
750 | |
---|
751 | switch ($action) { |
---|
752 | |
---|
753 | case 'new': |
---|
754 | |
---|
755 | $this->objects[$id] = array('t' => 'font', 'info' => array('name' => $options['name'], 'SubType' => 'Type1')); |
---|
756 | |
---|
757 | $fontNum = $this->numFonts; |
---|
758 | |
---|
759 | $this->objects[$id]['info']['fontNum'] = $fontNum; |
---|
760 | |
---|
761 | // deal with the encoding and the differences |
---|
762 | if (isset($options['differences'])) { |
---|
763 | |
---|
764 | // then we'll need an encoding dictionary |
---|
765 | $this->numObj++; |
---|
766 | |
---|
767 | $this->o_fontEncoding($this->numObj, 'new', $options); |
---|
768 | |
---|
769 | $this->objects[$id]['info']['encodingDictionary'] = $this->numObj; |
---|
770 | } else if (isset($options['encoding'])) { |
---|
771 | |
---|
772 | // we can specify encoding here |
---|
773 | switch ($options['encoding']) { |
---|
774 | |
---|
775 | case 'WinAnsiEncoding': |
---|
776 | |
---|
777 | case 'MacRomanEncoding': |
---|
778 | |
---|
779 | case 'MacExpertEncoding': |
---|
780 | |
---|
781 | $this->objects[$id]['info']['encoding'] = $options['encoding']; |
---|
782 | |
---|
783 | break; |
---|
784 | |
---|
785 | case 'none': |
---|
786 | |
---|
787 | break; |
---|
788 | |
---|
789 | default: |
---|
790 | |
---|
791 | $this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding'; |
---|
792 | |
---|
793 | break; |
---|
794 | } |
---|
795 | } else { |
---|
796 | |
---|
797 | $this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding'; |
---|
798 | } |
---|
799 | |
---|
800 | // also tell the pages node about the new font |
---|
801 | $this->o_pages($this->currentNode, 'font', array('fontNum' => $fontNum, 'objNum' => $id)); |
---|
802 | |
---|
803 | break; |
---|
804 | |
---|
805 | |
---|
806 | case 'add': |
---|
807 | |
---|
808 | foreach ($options as $k => $v) { |
---|
809 | |
---|
810 | switch ($k) { |
---|
811 | |
---|
812 | case 'BaseFont': |
---|
813 | |
---|
814 | $o['info']['name'] = $v; |
---|
815 | |
---|
816 | break; |
---|
817 | |
---|
818 | case 'FirstChar': |
---|
819 | |
---|
820 | case 'LastChar': |
---|
821 | |
---|
822 | case 'Widths': |
---|
823 | |
---|
824 | case 'FontDescriptor': |
---|
825 | |
---|
826 | case 'SubType': |
---|
827 | |
---|
828 | $this->addMessage('o_font '.$k." : ".$v); |
---|
829 | |
---|
830 | $o['info'][$k] = $v; |
---|
831 | |
---|
832 | break; |
---|
833 | } |
---|
834 | } |
---|
835 | |
---|
836 | break; |
---|
837 | |
---|
838 | |
---|
839 | case 'out': |
---|
840 | |
---|
841 | $res = "\n".$id." 0 obj\n<< /Type /Font\n/Subtype /".$o['info']['SubType']."\n"; |
---|
842 | |
---|
843 | $res.= "/Name /F".$o['info']['fontNum']."\n"; |
---|
844 | |
---|
845 | $res.= "/BaseFont /".$o['info']['name']."\n"; |
---|
846 | |
---|
847 | if (isset($o['info']['encodingDictionary'])) { |
---|
848 | |
---|
849 | // then place a reference to the dictionary |
---|
850 | $res.= "/Encoding ".$o['info']['encodingDictionary']." 0 R\n"; |
---|
851 | } else if (isset($o['info']['encoding'])) { |
---|
852 | |
---|
853 | // use the specified encoding |
---|
854 | $res.= "/Encoding /".$o['info']['encoding']."\n"; |
---|
855 | } |
---|
856 | |
---|
857 | if (isset($o['info']['FirstChar'])) { |
---|
858 | |
---|
859 | $res.= "/FirstChar ".$o['info']['FirstChar']."\n"; |
---|
860 | } |
---|
861 | |
---|
862 | if (isset($o['info']['LastChar'])) { |
---|
863 | |
---|
864 | $res.= "/LastChar ".$o['info']['LastChar']."\n"; |
---|
865 | } |
---|
866 | |
---|
867 | if (isset($o['info']['Widths'])) { |
---|
868 | |
---|
869 | $res.= "/Widths ".$o['info']['Widths']." 0 R\n"; |
---|
870 | } |
---|
871 | |
---|
872 | if (isset($o['info']['FontDescriptor'])) { |
---|
873 | |
---|
874 | $res.= "/FontDescriptor ".$o['info']['FontDescriptor']." 0 R\n"; |
---|
875 | } |
---|
876 | |
---|
877 | $res.= ">>\nendobj"; |
---|
878 | |
---|
879 | return $res; |
---|
880 | |
---|
881 | break; |
---|
882 | } |
---|
883 | } |
---|
884 | |
---|
885 | |
---|
886 | /** |
---|
887 | * a font descriptor, needed for including additional fonts |
---|
888 | */ |
---|
889 | function o_fontDescriptor($id, $action, $options = '') { |
---|
890 | |
---|
891 | if ($action != 'new') { |
---|
892 | |
---|
893 | $o = & $this->objects[$id]; |
---|
894 | } |
---|
895 | |
---|
896 | switch ($action) { |
---|
897 | |
---|
898 | case 'new': |
---|
899 | |
---|
900 | $this->objects[$id] = array('t'=>'fontDescriptor', 'info'=>$options); |
---|
901 | |
---|
902 | break; |
---|
903 | |
---|
904 | case 'out': |
---|
905 | |
---|
906 | $res = "\n".$id." 0 obj\n<< /Type /FontDescriptor\n"; |
---|
907 | |
---|
908 | foreach ($o['info'] as $label => $value) { |
---|
909 | |
---|
910 | switch ($label) { |
---|
911 | |
---|
912 | case 'Ascent': |
---|
913 | |
---|
914 | case 'CapHeight': |
---|
915 | |
---|
916 | case 'Descent': |
---|
917 | |
---|
918 | case 'Flags': |
---|
919 | |
---|
920 | case 'ItalicAngle': |
---|
921 | |
---|
922 | case 'StemV': |
---|
923 | |
---|
924 | case 'AvgWidth': |
---|
925 | |
---|
926 | case 'Leading': |
---|
927 | |
---|
928 | case 'MaxWidth': |
---|
929 | |
---|
930 | case 'MissingWidth': |
---|
931 | |
---|
932 | case 'StemH': |
---|
933 | |
---|
934 | case 'XHeight': |
---|
935 | |
---|
936 | case 'CharSet': |
---|
937 | |
---|
938 | if (strlen($value)) { |
---|
939 | |
---|
940 | $res.= '/'.$label.' '.$value."\n"; |
---|
941 | } |
---|
942 | |
---|
943 | break; |
---|
944 | |
---|
945 | case 'FontFile': |
---|
946 | |
---|
947 | case 'FontFile2': |
---|
948 | |
---|
949 | case 'FontFile3': |
---|
950 | |
---|
951 | $res.= '/'.$label.' '.$value." 0 R\n"; |
---|
952 | |
---|
953 | break; |
---|
954 | |
---|
955 | case 'FontBBox': |
---|
956 | |
---|
957 | $res.= '/'.$label.' ['.$value[0].' '.$value[1].' '.$value[2].' '.$value[3]."]\n"; |
---|
958 | |
---|
959 | break; |
---|
960 | |
---|
961 | case 'FontName': |
---|
962 | |
---|
963 | $res.= '/'.$label.' /'.$value."\n"; |
---|
964 | |
---|
965 | break; |
---|
966 | } |
---|
967 | } |
---|
968 | |
---|
969 | $res.= ">>\nendobj"; |
---|
970 | |
---|
971 | return $res; |
---|
972 | |
---|
973 | break; |
---|
974 | } |
---|
975 | } |
---|
976 | |
---|
977 | |
---|
978 | /** |
---|
979 | * the font encoding |
---|
980 | */ |
---|
981 | function o_fontEncoding($id, $action, $options = '') { |
---|
982 | |
---|
983 | if ($action != 'new') { |
---|
984 | |
---|
985 | $o = & $this->objects[$id]; |
---|
986 | } |
---|
987 | |
---|
988 | switch ($action) { |
---|
989 | |
---|
990 | case 'new': |
---|
991 | |
---|
992 | // the options array should contain 'differences' and maybe 'encoding' |
---|
993 | $this->objects[$id] = array('t'=>'fontEncoding', 'info'=>$options); |
---|
994 | |
---|
995 | break; |
---|
996 | |
---|
997 | case 'out': |
---|
998 | |
---|
999 | $res = "\n".$id." 0 obj\n<< /Type /Encoding\n"; |
---|
1000 | |
---|
1001 | if (!isset($o['info']['encoding'])) { |
---|
1002 | |
---|
1003 | $o['info']['encoding'] = 'WinAnsiEncoding'; |
---|
1004 | } |
---|
1005 | |
---|
1006 | if ($o['info']['encoding'] != 'none') { |
---|
1007 | |
---|
1008 | $res.= "/BaseEncoding /".$o['info']['encoding']."\n"; |
---|
1009 | } |
---|
1010 | |
---|
1011 | $res.= "/Differences \n["; |
---|
1012 | |
---|
1013 | $onum = -100; |
---|
1014 | |
---|
1015 | foreach($o['info']['differences'] as $num=>$label) { |
---|
1016 | |
---|
1017 | if ($num != $onum+1) { |
---|
1018 | |
---|
1019 | // we cannot make use of consecutive numbering |
---|
1020 | $res.= "\n".$num." /".$label; |
---|
1021 | } else { |
---|
1022 | |
---|
1023 | $res.= " /".$label; |
---|
1024 | } |
---|
1025 | |
---|
1026 | $onum = $num; |
---|
1027 | } |
---|
1028 | |
---|
1029 | $res.= "\n]\n>>\nendobj"; |
---|
1030 | |
---|
1031 | return $res; |
---|
1032 | |
---|
1033 | break; |
---|
1034 | } |
---|
1035 | } |
---|
1036 | |
---|
1037 | |
---|
1038 | /** |
---|
1039 | * the document procset, solves some problems with printing to old PS printers |
---|
1040 | */ |
---|
1041 | function o_procset($id, $action, $options = '') { |
---|
1042 | |
---|
1043 | if ($action != 'new') { |
---|
1044 | |
---|
1045 | $o = & $this->objects[$id]; |
---|
1046 | } |
---|
1047 | |
---|
1048 | switch ($action) { |
---|
1049 | |
---|
1050 | case 'new': |
---|
1051 | |
---|
1052 | $this->objects[$id] = array('t'=>'procset', 'info'=>array('PDF'=>1, 'Text'=>1)); |
---|
1053 | |
---|
1054 | $this->o_pages($this->currentNode, 'procset', $id); |
---|
1055 | |
---|
1056 | $this->procsetObjectId = $id; |
---|
1057 | |
---|
1058 | break; |
---|
1059 | |
---|
1060 | case 'add': |
---|
1061 | |
---|
1062 | // this is to add new items to the procset list, despite the fact that this is considered |
---|
1063 | // obselete, the items are required for printing to some postscript printers |
---|
1064 | switch ($options) { |
---|
1065 | |
---|
1066 | case 'ImageB': |
---|
1067 | |
---|
1068 | case 'ImageC': |
---|
1069 | |
---|
1070 | case 'ImageI': |
---|
1071 | |
---|
1072 | $o['info'][$options] = 1; |
---|
1073 | |
---|
1074 | break; |
---|
1075 | } |
---|
1076 | |
---|
1077 | break; |
---|
1078 | |
---|
1079 | case 'out': |
---|
1080 | |
---|
1081 | $res = "\n".$id." 0 obj\n["; |
---|
1082 | |
---|
1083 | foreach ($o['info'] as $label=>$val) { |
---|
1084 | |
---|
1085 | $res.= '/'.$label.' '; |
---|
1086 | } |
---|
1087 | |
---|
1088 | $res.= "]\nendobj"; |
---|
1089 | |
---|
1090 | return $res; |
---|
1091 | |
---|
1092 | break; |
---|
1093 | } |
---|
1094 | } |
---|
1095 | |
---|
1096 | |
---|
1097 | /** |
---|
1098 | * define the document information |
---|
1099 | */ |
---|
1100 | function o_info($id, $action, $options = '') { |
---|
1101 | |
---|
1102 | if ($action != 'new') { |
---|
1103 | |
---|
1104 | $o = & $this->objects[$id]; |
---|
1105 | } |
---|
1106 | |
---|
1107 | switch ($action) { |
---|
1108 | |
---|
1109 | case 'new': |
---|
1110 | |
---|
1111 | $this->infoObject = $id; |
---|
1112 | |
---|
1113 | $date = 'D:'.@date('Ymd'); |
---|
1114 | |
---|
1115 | $this->objects[$id] = array('t'=>'info', 'info'=>array('Creator'=>'R and OS php pdf writer, http://www.ros.co.nz', 'CreationDate'=>$date)); |
---|
1116 | |
---|
1117 | break; |
---|
1118 | |
---|
1119 | case 'Title': |
---|
1120 | |
---|
1121 | case 'Author': |
---|
1122 | |
---|
1123 | case 'Subject': |
---|
1124 | |
---|
1125 | case 'Keywords': |
---|
1126 | |
---|
1127 | case 'Creator': |
---|
1128 | |
---|
1129 | case 'Producer': |
---|
1130 | |
---|
1131 | case 'CreationDate': |
---|
1132 | |
---|
1133 | case 'ModDate': |
---|
1134 | |
---|
1135 | case 'Trapped': |
---|
1136 | |
---|
1137 | $o['info'][$action] = $options; |
---|
1138 | |
---|
1139 | break; |
---|
1140 | |
---|
1141 | case 'out': |
---|
1142 | |
---|
1143 | if ($this->encrypted) { |
---|
1144 | |
---|
1145 | $this->encryptInit($id); |
---|
1146 | } |
---|
1147 | |
---|
1148 | $res = "\n".$id." 0 obj\n<<\n"; |
---|
1149 | |
---|
1150 | foreach ($o['info'] as $k=>$v) { |
---|
1151 | |
---|
1152 | $res.= '/'.$k.' ('; |
---|
1153 | |
---|
1154 | if ($this->encrypted) { |
---|
1155 | |
---|
1156 | $res.= $this->filterText($this->ARC4($v)); |
---|
1157 | } else { |
---|
1158 | |
---|
1159 | $res.= $this->filterText($v); |
---|
1160 | } |
---|
1161 | |
---|
1162 | $res.= ")\n"; |
---|
1163 | } |
---|
1164 | |
---|
1165 | $res.= ">>\nendobj"; |
---|
1166 | |
---|
1167 | return $res; |
---|
1168 | |
---|
1169 | break; |
---|
1170 | } |
---|
1171 | } |
---|
1172 | |
---|
1173 | |
---|
1174 | /** |
---|
1175 | * an action object, used to link to URLS initially |
---|
1176 | */ |
---|
1177 | function o_action($id, $action, $options = '') { |
---|
1178 | |
---|
1179 | if ($action != 'new') { |
---|
1180 | |
---|
1181 | $o = & $this->objects[$id]; |
---|
1182 | } |
---|
1183 | |
---|
1184 | switch ($action) { |
---|
1185 | |
---|
1186 | case 'new': |
---|
1187 | |
---|
1188 | if (is_array($options)) { |
---|
1189 | |
---|
1190 | $this->objects[$id] = array('t'=>'action', 'info'=>$options, 'type'=>$options['type']); |
---|
1191 | } else { |
---|
1192 | |
---|
1193 | // then assume a URI action |
---|
1194 | $this->objects[$id] = array('t'=>'action', 'info'=>$options, 'type'=>'URI'); |
---|
1195 | } |
---|
1196 | |
---|
1197 | break; |
---|
1198 | |
---|
1199 | case 'out': |
---|
1200 | |
---|
1201 | if ($this->encrypted) { |
---|
1202 | |
---|
1203 | $this->encryptInit($id); |
---|
1204 | } |
---|
1205 | |
---|
1206 | $res = "\n".$id." 0 obj\n<< /Type /Action"; |
---|
1207 | |
---|
1208 | switch ($o['type']) { |
---|
1209 | |
---|
1210 | case 'ilink': |
---|
1211 | |
---|
1212 | // there will be an 'label' setting, this is the name of the destination |
---|
1213 | $res.= "\n/S /GoTo\n/D ".$this->destinations[(string)$o['info']['label']]." 0 R"; |
---|
1214 | |
---|
1215 | break; |
---|
1216 | |
---|
1217 | case 'URI': |
---|
1218 | |
---|
1219 | $res.= "\n/S /URI\n/URI ("; |
---|
1220 | |
---|
1221 | if ($this->encrypted) { |
---|
1222 | |
---|
1223 | $res.= $this->filterText($this->ARC4($o['info'])); |
---|
1224 | } else { |
---|
1225 | |
---|
1226 | $res.= $this->filterText($o['info']); |
---|
1227 | } |
---|
1228 | |
---|
1229 | $res.= ")"; |
---|
1230 | |
---|
1231 | break; |
---|
1232 | } |
---|
1233 | |
---|
1234 | $res.= "\n>>\nendobj"; |
---|
1235 | |
---|
1236 | return $res; |
---|
1237 | |
---|
1238 | break; |
---|
1239 | } |
---|
1240 | } |
---|
1241 | |
---|
1242 | |
---|
1243 | /** |
---|
1244 | * an annotation object, this will add an annotation to the current page. |
---|
1245 | * initially will support just link annotations |
---|
1246 | */ |
---|
1247 | function o_annotation($id, $action, $options = '') { |
---|
1248 | |
---|
1249 | if ($action != 'new') { |
---|
1250 | |
---|
1251 | $o = & $this->objects[$id]; |
---|
1252 | } |
---|
1253 | |
---|
1254 | switch ($action) { |
---|
1255 | |
---|
1256 | case 'new': |
---|
1257 | |
---|
1258 | // add the annotation to the current page |
---|
1259 | $pageId = $this->currentPage; |
---|
1260 | |
---|
1261 | $this->o_page($pageId, 'annot', $id); |
---|
1262 | |
---|
1263 | // and add the action object which is going to be required |
---|
1264 | switch ($options['type']) { |
---|
1265 | |
---|
1266 | case 'link': |
---|
1267 | |
---|
1268 | $this->objects[$id] = array('t'=>'annotation', 'info'=>$options); |
---|
1269 | |
---|
1270 | $this->numObj++; |
---|
1271 | |
---|
1272 | $this->o_action($this->numObj, 'new', $options['url']); |
---|
1273 | |
---|
1274 | $this->objects[$id]['info']['actionId'] = $this->numObj; |
---|
1275 | |
---|
1276 | break; |
---|
1277 | |
---|
1278 | case 'ilink': |
---|
1279 | |
---|
1280 | // this is to a named internal link |
---|
1281 | $label = $options['label']; |
---|
1282 | |
---|
1283 | $this->objects[$id] = array('t'=>'annotation', 'info'=>$options); |
---|
1284 | |
---|
1285 | $this->numObj++; |
---|
1286 | |
---|
1287 | $this->o_action($this->numObj, 'new', array('type'=>'ilink', 'label'=>$label)); |
---|
1288 | |
---|
1289 | $this->objects[$id]['info']['actionId'] = $this->numObj; |
---|
1290 | |
---|
1291 | break; |
---|
1292 | } |
---|
1293 | |
---|
1294 | break; |
---|
1295 | |
---|
1296 | case 'out': |
---|
1297 | |
---|
1298 | $res = "\n".$id." 0 obj\n<< /Type /Annot"; |
---|
1299 | |
---|
1300 | switch ($o['info']['type']) { |
---|
1301 | |
---|
1302 | case 'link': |
---|
1303 | |
---|
1304 | case 'ilink': |
---|
1305 | |
---|
1306 | $res.= "\n/Subtype /Link"; |
---|
1307 | |
---|
1308 | break; |
---|
1309 | } |
---|
1310 | |
---|
1311 | $res.= "\n/A ".$o['info']['actionId']." 0 R"; |
---|
1312 | |
---|
1313 | $res.= "\n/Border [0 0 0]"; |
---|
1314 | |
---|
1315 | $res.= "\n/H /I"; |
---|
1316 | |
---|
1317 | $res.= "\n/Rect [ "; |
---|
1318 | |
---|
1319 | foreach($o['info']['rect'] as $v) { |
---|
1320 | |
---|
1321 | $res.= sprintf("%.4f ", $v); |
---|
1322 | } |
---|
1323 | |
---|
1324 | $res.= "]"; |
---|
1325 | |
---|
1326 | $res.= "\n>>\nendobj"; |
---|
1327 | |
---|
1328 | return $res; |
---|
1329 | |
---|
1330 | break; |
---|
1331 | } |
---|
1332 | } |
---|
1333 | |
---|
1334 | |
---|
1335 | /** |
---|
1336 | * a page object, it also creates a contents object to hold its contents |
---|
1337 | */ |
---|
1338 | function o_page($id, $action, $options = '') { |
---|
1339 | |
---|
1340 | if ($action != 'new') { |
---|
1341 | |
---|
1342 | $o = & $this->objects[$id]; |
---|
1343 | } |
---|
1344 | |
---|
1345 | switch ($action) { |
---|
1346 | |
---|
1347 | case 'new': |
---|
1348 | |
---|
1349 | $this->numPages++; |
---|
1350 | |
---|
1351 | $this->objects[$id] = array('t'=>'page', 'info'=>array('parent'=>$this->currentNode, 'pageNum'=>$this->numPages)); |
---|
1352 | |
---|
1353 | if (is_array($options)) { |
---|
1354 | |
---|
1355 | // then this must be a page insertion, array shoudl contain 'rid','pos'=[before|after] |
---|
1356 | $options['id'] = $id; |
---|
1357 | |
---|
1358 | $this->o_pages($this->currentNode, 'page', $options); |
---|
1359 | } else { |
---|
1360 | |
---|
1361 | $this->o_pages($this->currentNode, 'page', $id); |
---|
1362 | } |
---|
1363 | |
---|
1364 | $this->currentPage = $id; |
---|
1365 | |
---|
1366 | //make a contents object to go with this page |
---|
1367 | $this->numObj++; |
---|
1368 | |
---|
1369 | $this->o_contents($this->numObj, 'new', $id); |
---|
1370 | |
---|
1371 | $this->currentContents = $this->numObj; |
---|
1372 | |
---|
1373 | $this->objects[$id]['info']['contents'] = array(); |
---|
1374 | |
---|
1375 | $this->objects[$id]['info']['contents'][] = $this->numObj; |
---|
1376 | |
---|
1377 | $match = ($this->numPages%2 ? 'odd' : 'even'); |
---|
1378 | |
---|
1379 | foreach($this->addLooseObjects as $oId=>$target) { |
---|
1380 | |
---|
1381 | if ($target == 'all' || $match == $target) { |
---|
1382 | |
---|
1383 | $this->objects[$id]['info']['contents'][] = $oId; |
---|
1384 | } |
---|
1385 | } |
---|
1386 | |
---|
1387 | break; |
---|
1388 | |
---|
1389 | case 'content': |
---|
1390 | |
---|
1391 | $o['info']['contents'][] = $options; |
---|
1392 | |
---|
1393 | break; |
---|
1394 | |
---|
1395 | case 'annot': |
---|
1396 | |
---|
1397 | // add an annotation to this page |
---|
1398 | if (!isset($o['info']['annot'])) { |
---|
1399 | |
---|
1400 | $o['info']['annot'] = array(); |
---|
1401 | } |
---|
1402 | |
---|
1403 | // $options should contain the id of the annotation dictionary |
---|
1404 | $o['info']['annot'][] = $options; |
---|
1405 | |
---|
1406 | break; |
---|
1407 | |
---|
1408 | case 'out': |
---|
1409 | |
---|
1410 | $res = "\n".$id." 0 obj\n<< /Type /Page"; |
---|
1411 | |
---|
1412 | $res.= "\n/Parent ".$o['info']['parent']." 0 R"; |
---|
1413 | |
---|
1414 | if (isset($o['info']['annot'])) { |
---|
1415 | |
---|
1416 | $res.= "\n/Annots ["; |
---|
1417 | |
---|
1418 | foreach($o['info']['annot'] as $aId) { |
---|
1419 | |
---|
1420 | $res.= " ".$aId." 0 R"; |
---|
1421 | } |
---|
1422 | |
---|
1423 | $res.= " ]"; |
---|
1424 | } |
---|
1425 | |
---|
1426 | $count = count($o['info']['contents']); |
---|
1427 | |
---|
1428 | if ($count == 1) { |
---|
1429 | |
---|
1430 | $res.= "\n/Contents ".$o['info']['contents'][0]." 0 R"; |
---|
1431 | } else if ($count>1) { |
---|
1432 | |
---|
1433 | $res.= "\n/Contents [\n"; |
---|
1434 | |
---|
1435 | // reverse the page contents so added objects are below normal content |
---|
1436 | //foreach (array_reverse($o['info']['contents']) as $cId) { |
---|
1437 | |
---|
1438 | // Back to normal now that I've got transparency working --Benj |
---|
1439 | foreach ($o['info']['contents'] as $cId) { |
---|
1440 | $res.= $cId." 0 R\n"; |
---|
1441 | } |
---|
1442 | |
---|
1443 | $res.= "]"; |
---|
1444 | } |
---|
1445 | |
---|
1446 | $res.= "\n>>\nendobj"; |
---|
1447 | |
---|
1448 | return $res; |
---|
1449 | |
---|
1450 | break; |
---|
1451 | } |
---|
1452 | } |
---|
1453 | |
---|
1454 | |
---|
1455 | /** |
---|
1456 | * the contents objects hold all of the content which appears on pages |
---|
1457 | */ |
---|
1458 | function o_contents($id, $action, $options = '') { |
---|
1459 | |
---|
1460 | if ($action != 'new') { |
---|
1461 | |
---|
1462 | $o = & $this->objects[$id]; |
---|
1463 | } |
---|
1464 | |
---|
1465 | switch ($action) { |
---|
1466 | |
---|
1467 | case 'new': |
---|
1468 | |
---|
1469 | $this->objects[$id] = array('t'=>'contents', 'c'=>'', 'info'=>array()); |
---|
1470 | |
---|
1471 | if (strlen($options) && intval($options)) { |
---|
1472 | |
---|
1473 | // then this contents is the primary for a page |
---|
1474 | $this->objects[$id]['onPage'] = $options; |
---|
1475 | } else if ($options == 'raw') { |
---|
1476 | |
---|
1477 | // then this page contains some other type of system object |
---|
1478 | $this->objects[$id]['raw'] = 1; |
---|
1479 | } |
---|
1480 | |
---|
1481 | break; |
---|
1482 | |
---|
1483 | case 'add': |
---|
1484 | |
---|
1485 | // add more options to the decleration |
---|
1486 | foreach ($options as $k=>$v) { |
---|
1487 | |
---|
1488 | $o['info'][$k] = $v; |
---|
1489 | } |
---|
1490 | |
---|
1491 | case 'out': |
---|
1492 | |
---|
1493 | $tmp = $o['c']; |
---|
1494 | |
---|
1495 | $res = "\n".$id." 0 obj\n"; |
---|
1496 | |
---|
1497 | if (isset($this->objects[$id]['raw'])) { |
---|
1498 | |
---|
1499 | $res.= $tmp; |
---|
1500 | } else { |
---|
1501 | |
---|
1502 | $res.= "<<"; |
---|
1503 | |
---|
1504 | if (function_exists('gzcompress') && $this->options['compression']) { |
---|
1505 | |
---|
1506 | // then implement ZLIB based compression on this content stream |
---|
1507 | $res.= " /Filter /FlateDecode"; |
---|
1508 | |
---|
1509 | $tmp = gzcompress($tmp, 6); |
---|
1510 | } |
---|
1511 | |
---|
1512 | if ($this->encrypted) { |
---|
1513 | |
---|
1514 | $this->encryptInit($id); |
---|
1515 | |
---|
1516 | $tmp = $this->ARC4($tmp); |
---|
1517 | } |
---|
1518 | |
---|
1519 | foreach($o['info'] as $k=>$v) { |
---|
1520 | |
---|
1521 | $res.= "\n/".$k.' '.$v; |
---|
1522 | } |
---|
1523 | |
---|
1524 | $res.= "\n/Length ".strlen($tmp) ." >>\nstream\n".$tmp."\nendstream"; |
---|
1525 | } |
---|
1526 | |
---|
1527 | $res.= "\nendobj\n"; |
---|
1528 | |
---|
1529 | return $res; |
---|
1530 | |
---|
1531 | break; |
---|
1532 | } |
---|
1533 | } |
---|
1534 | |
---|
1535 | |
---|
1536 | /** |
---|
1537 | * an image object, will be an XObject in the document, includes description and data |
---|
1538 | */ |
---|
1539 | function o_image($id, $action, $options = '') { |
---|
1540 | |
---|
1541 | if ($action != 'new') { |
---|
1542 | |
---|
1543 | $o = & $this->objects[$id]; |
---|
1544 | } |
---|
1545 | |
---|
1546 | switch ($action) { |
---|
1547 | |
---|
1548 | case 'new': |
---|
1549 | |
---|
1550 | // make the new object |
---|
1551 | $this->objects[$id] = array('t'=>'image', 'data'=>&$options['data'], 'info'=>array()); |
---|
1552 | |
---|
1553 | $this->objects[$id]['info']['Type'] = '/XObject'; |
---|
1554 | |
---|
1555 | $this->objects[$id]['info']['Subtype'] = '/Image'; |
---|
1556 | |
---|
1557 | $this->objects[$id]['info']['Width'] = $options['iw']; |
---|
1558 | |
---|
1559 | $this->objects[$id]['info']['Height'] = $options['ih']; |
---|
1560 | |
---|
1561 | if (!isset($options['type']) || $options['type'] == 'jpg') { |
---|
1562 | |
---|
1563 | if (!isset($options['channels'])) { |
---|
1564 | |
---|
1565 | $options['channels'] = 3; |
---|
1566 | } |
---|
1567 | |
---|
1568 | switch ($options['channels']) { |
---|
1569 | |
---|
1570 | case 1: |
---|
1571 | |
---|
1572 | $this->objects[$id]['info']['ColorSpace'] = '/DeviceGray'; |
---|
1573 | |
---|
1574 | break; |
---|
1575 | |
---|
1576 | default: |
---|
1577 | |
---|
1578 | $this->objects[$id]['info']['ColorSpace'] = '/DeviceRGB'; |
---|
1579 | |
---|
1580 | break; |
---|
1581 | } |
---|
1582 | |
---|
1583 | $this->objects[$id]['info']['Filter'] = '/DCTDecode'; |
---|
1584 | |
---|
1585 | $this->objects[$id]['info']['BitsPerComponent'] = 8; |
---|
1586 | } else if ($options['type'] == 'png') { |
---|
1587 | |
---|
1588 | $this->objects[$id]['info']['Filter'] = '/FlateDecode'; |
---|
1589 | |
---|
1590 | $this->objects[$id]['info']['DecodeParms'] = '<< /Predictor 15 /Colors '.$options['ncolor'].' /Columns '.$options['iw'].' /BitsPerComponent '.$options['bitsPerComponent'].'>>'; |
---|
1591 | if (strlen($options['pdata'])) { |
---|
1592 | |
---|
1593 | $tmp = ' [ /Indexed /DeviceRGB '.(strlen($options['pdata']) /3-1) .' '; |
---|
1594 | |
---|
1595 | $this->numObj++; |
---|
1596 | |
---|
1597 | $this->o_contents($this->numObj, 'new'); |
---|
1598 | |
---|
1599 | $this->objects[$this->numObj]['c'] = $options['pdata']; |
---|
1600 | |
---|
1601 | $tmp.= $this->numObj.' 0 R'; |
---|
1602 | |
---|
1603 | $tmp.= ' ]'; |
---|
1604 | |
---|
1605 | $this->objects[$id]['info']['ColorSpace'] = $tmp; |
---|
1606 | |
---|
1607 | if (isset($options['transparency'])) { |
---|
1608 | |
---|
1609 | switch ($options['transparency']['type']) { |
---|
1610 | |
---|
1611 | case 'indexed': |
---|
1612 | |
---|
1613 | $tmp = ' [ '.$options['transparency']['data'].' '.$options['transparency']['data'].'] '; |
---|
1614 | |
---|
1615 | $this->objects[$id]['info']['Mask'] = $tmp; |
---|
1616 | |
---|
1617 | break; |
---|
1618 | |
---|
1619 | case 'color-key': |
---|
1620 | $tmp = ' [ '. |
---|
1621 | $options['transparency']['r'] . ' ' . $options['transparency']['r'] . |
---|
1622 | $options['transparency']['g'] . ' ' . $options['transparency']['g'] . |
---|
1623 | $options['transparency']['b'] . ' ' . $options['transparency']['b'] . |
---|
1624 | ' ] '; |
---|
1625 | $this->objects[$id]['info']['Mask'] = $tmp; |
---|
1626 | pre_r($tmp); |
---|
1627 | break; |
---|
1628 | |
---|
1629 | } |
---|
1630 | } |
---|
1631 | } else { |
---|
1632 | |
---|
1633 | if (isset($options['transparency'])) { |
---|
1634 | |
---|
1635 | switch ($options['transparency']['type']) { |
---|
1636 | |
---|
1637 | case 'indexed': |
---|
1638 | |
---|
1639 | $tmp = ' [ '.$options['transparency']['data'].' '.$options['transparency']['data'].'] '; |
---|
1640 | |
---|
1641 | $this->objects[$id]['info']['Mask'] = $tmp; |
---|
1642 | |
---|
1643 | break; |
---|
1644 | |
---|
1645 | case 'color-key': |
---|
1646 | $tmp = ' [ '. |
---|
1647 | $options['transparency']['r'] . ' ' . $options['transparency']['r'] . ' ' . |
---|
1648 | $options['transparency']['g'] . ' ' . $options['transparency']['g'] . ' ' . |
---|
1649 | $options['transparency']['b'] . ' ' . $options['transparency']['b'] . |
---|
1650 | ' ] '; |
---|
1651 | $this->objects[$id]['info']['Mask'] = $tmp; |
---|
1652 | break; |
---|
1653 | |
---|
1654 | } |
---|
1655 | } |
---|
1656 | $this->objects[$id]['info']['ColorSpace'] = '/'.$options['color']; |
---|
1657 | } |
---|
1658 | |
---|
1659 | $this->objects[$id]['info']['BitsPerComponent'] = $options['bitsPerComponent']; |
---|
1660 | } |
---|
1661 | |
---|
1662 | // assign it a place in the named resource dictionary as an external object, according to |
---|
1663 | // the label passed in with it. |
---|
1664 | $this->o_pages($this->currentNode, 'xObject', array('label'=>$options['label'], 'objNum'=>$id)); |
---|
1665 | |
---|
1666 | // also make sure that we have the right procset object for it. |
---|
1667 | $this->o_procset($this->procsetObjectId, 'add', 'ImageC'); |
---|
1668 | |
---|
1669 | break; |
---|
1670 | |
---|
1671 | case 'out': |
---|
1672 | |
---|
1673 | $tmp = &$o['data']; |
---|
1674 | |
---|
1675 | $res = "\n".$id." 0 obj\n<<"; |
---|
1676 | |
---|
1677 | foreach($o['info'] as $k=>$v) { |
---|
1678 | |
---|
1679 | $res.= "\n/".$k.' '.$v; |
---|
1680 | } |
---|
1681 | |
---|
1682 | if ($this->encrypted) { |
---|
1683 | |
---|
1684 | $this->encryptInit($id); |
---|
1685 | |
---|
1686 | $tmp = $this->ARC4($tmp); |
---|
1687 | } |
---|
1688 | |
---|
1689 | $res.= "\n/Length ".strlen($tmp) ." >>\nstream\n".$tmp."\nendstream\nendobj\n"; |
---|
1690 | |
---|
1691 | return $res; |
---|
1692 | |
---|
1693 | break; |
---|
1694 | } |
---|
1695 | } |
---|
1696 | |
---|
1697 | |
---|
1698 | /** |
---|
1699 | * graphics state object |
---|
1700 | */ |
---|
1701 | function o_extGState($id, $action, $options = "") { |
---|
1702 | |
---|
1703 | static $valid_params = array("LW", "LC", "LC", "LJ", "ML", |
---|
1704 | "D", "RI", "OP", "op", "OPM", |
---|
1705 | "Font", "BG", "BG2", "UCR", |
---|
1706 | "TR", "TR2", "HT", "FL", |
---|
1707 | "SM", "SA", "BM", "SMask", |
---|
1708 | "CA", "ca", "AIS", "TK"); |
---|
1709 | |
---|
1710 | if ( $action != "new") { |
---|
1711 | $o = & $this->objects[$id]; |
---|
1712 | } |
---|
1713 | |
---|
1714 | switch ($action) { |
---|
1715 | |
---|
1716 | case "new": |
---|
1717 | $this->objects[$id] = array('t' => 'extGState', 'info' => $options); |
---|
1718 | |
---|
1719 | // Tell the pages about the new resource |
---|
1720 | $this->numStates++; |
---|
1721 | $this->o_pages($this->currentNode, 'extGState', array("objNum" => $id, "stateNum" => $this->numStates)); |
---|
1722 | break; |
---|
1723 | |
---|
1724 | |
---|
1725 | case "out": |
---|
1726 | $res = |
---|
1727 | "\n" . $id . " 0 obj\n". |
---|
1728 | "<< /Type /ExtGState\n"; |
---|
1729 | |
---|
1730 | foreach ($o["info"] as $parameter => $value) { |
---|
1731 | if ( !in_array($parameter, $valid_params)) |
---|
1732 | continue; |
---|
1733 | $res.= "/$parameter $value\n"; |
---|
1734 | } |
---|
1735 | |
---|
1736 | $res.= |
---|
1737 | ">>\n". |
---|
1738 | "endobj"; |
---|
1739 | |
---|
1740 | return $res; |
---|
1741 | } |
---|
1742 | } |
---|
1743 | |
---|
1744 | |
---|
1745 | /** |
---|
1746 | * encryption object. |
---|
1747 | */ |
---|
1748 | function o_encryption($id, $action, $options = '') { |
---|
1749 | |
---|
1750 | if ($action != 'new') { |
---|
1751 | |
---|
1752 | $o = & $this->objects[$id]; |
---|
1753 | } |
---|
1754 | |
---|
1755 | switch ($action) { |
---|
1756 | |
---|
1757 | case 'new': |
---|
1758 | |
---|
1759 | // make the new object |
---|
1760 | $this->objects[$id] = array('t'=>'encryption', 'info'=>$options); |
---|
1761 | |
---|
1762 | $this->arc4_objnum = $id; |
---|
1763 | |
---|
1764 | // figure out the additional paramaters required |
---|
1765 | $pad = chr(0x28) .chr(0xBF) .chr(0x4E) .chr(0x5E) .chr(0x4E) .chr(0x75) .chr(0x8A) .chr(0x41) .chr(0x64) .chr(0x00) .chr(0x4E) .chr(0x56) .chr(0xFF) .chr(0xFA) .chr(0x01) .chr(0x08) .chr(0x2E) .chr(0x2E) .chr(0x00) .chr(0xB6) .chr(0xD0) .chr(0x68) .chr(0x3E) .chr(0x80) .chr(0x2F) .chr(0x0C) .chr(0xA9) .chr(0xFE) .chr(0x64) .chr(0x53) .chr(0x69) .chr(0x7A); |
---|
1766 | |
---|
1767 | $len = strlen($options['owner']); |
---|
1768 | |
---|
1769 | if ($len>32) { |
---|
1770 | |
---|
1771 | $owner = substr($options['owner'], 0, 32); |
---|
1772 | } else if ($len<32) { |
---|
1773 | |
---|
1774 | $owner = $options['owner'].substr($pad, 0, 32-$len); |
---|
1775 | } else { |
---|
1776 | |
---|
1777 | $owner = $options['owner']; |
---|
1778 | } |
---|
1779 | |
---|
1780 | $len = strlen($options['user']); |
---|
1781 | |
---|
1782 | if ($len>32) { |
---|
1783 | |
---|
1784 | $user = substr($options['user'], 0, 32); |
---|
1785 | } else if ($len<32) { |
---|
1786 | |
---|
1787 | $user = $options['user'].substr($pad, 0, 32-$len); |
---|
1788 | } else { |
---|
1789 | |
---|
1790 | $user = $options['user']; |
---|
1791 | } |
---|
1792 | |
---|
1793 | $tmp = $this->md5_16($owner); |
---|
1794 | |
---|
1795 | $okey = substr($tmp, 0, 5); |
---|
1796 | |
---|
1797 | $this->ARC4_init($okey); |
---|
1798 | |
---|
1799 | $ovalue = $this->ARC4($user); |
---|
1800 | |
---|
1801 | $this->objects[$id]['info']['O'] = $ovalue; |
---|
1802 | |
---|
1803 | // now make the u value, phew. |
---|
1804 | $tmp = $this->md5_16($user.$ovalue.chr($options['p']) .chr(255) .chr(255) .chr(255) .$this->fileIdentifier); |
---|
1805 | |
---|
1806 | $ukey = substr($tmp, 0, 5); |
---|
1807 | |
---|
1808 | |
---|
1809 | $this->ARC4_init($ukey); |
---|
1810 | |
---|
1811 | $this->encryptionKey = $ukey; |
---|
1812 | |
---|
1813 | $this->encrypted = 1; |
---|
1814 | |
---|
1815 | $uvalue = $this->ARC4($pad); |
---|
1816 | |
---|
1817 | |
---|
1818 | $this->objects[$id]['info']['U'] = $uvalue; |
---|
1819 | |
---|
1820 | $this->encryptionKey = $ukey; |
---|
1821 | |
---|
1822 | |
---|
1823 | // initialize the arc4 array |
---|
1824 | break; |
---|
1825 | |
---|
1826 | case 'out': |
---|
1827 | |
---|
1828 | $res = "\n".$id." 0 obj\n<<"; |
---|
1829 | |
---|
1830 | $res.= "\n/Filter /Standard"; |
---|
1831 | |
---|
1832 | $res.= "\n/V 1"; |
---|
1833 | |
---|
1834 | $res.= "\n/R 2"; |
---|
1835 | |
---|
1836 | $res.= "\n/O (".$this->filterText($o['info']['O']) .')'; |
---|
1837 | |
---|
1838 | $res.= "\n/U (".$this->filterText($o['info']['U']) .')'; |
---|
1839 | |
---|
1840 | // and the p-value needs to be converted to account for the twos-complement approach |
---|
1841 | $o['info']['p'] = (($o['info']['p']^255) +1) *-1; |
---|
1842 | |
---|
1843 | $res.= "\n/P ".($o['info']['p']); |
---|
1844 | |
---|
1845 | $res.= "\n>>\nendobj\n"; |
---|
1846 | |
---|
1847 | |
---|
1848 | return $res; |
---|
1849 | |
---|
1850 | break; |
---|
1851 | } |
---|
1852 | } |
---|
1853 | |
---|
1854 | |
---|
1855 | /** |
---|
1856 | * ARC4 functions |
---|
1857 | * A series of function to implement ARC4 encoding in PHP |
---|
1858 | */ |
---|
1859 | |
---|
1860 | /** |
---|
1861 | * calculate the 16 byte version of the 128 bit md5 digest of the string |
---|
1862 | */ |
---|
1863 | function md5_16($string) { |
---|
1864 | |
---|
1865 | $tmp = md5($string); |
---|
1866 | |
---|
1867 | $out = ''; |
---|
1868 | |
---|
1869 | for ($i = 0;$i <= 30;$i = $i+2) { |
---|
1870 | |
---|
1871 | $out.= chr(hexdec(substr($tmp, $i, 2))); |
---|
1872 | } |
---|
1873 | |
---|
1874 | return $out; |
---|
1875 | } |
---|
1876 | |
---|
1877 | |
---|
1878 | /** |
---|
1879 | * initialize the encryption for processing a particular object |
---|
1880 | */ |
---|
1881 | function encryptInit($id) { |
---|
1882 | |
---|
1883 | $tmp = $this->encryptionKey; |
---|
1884 | |
---|
1885 | $hex = dechex($id); |
---|
1886 | |
---|
1887 | if (strlen($hex) <6) { |
---|
1888 | |
---|
1889 | $hex = substr('000000', 0, 6-strlen($hex)) .$hex; |
---|
1890 | } |
---|
1891 | |
---|
1892 | $tmp.= chr(hexdec(substr($hex, 4, 2))) .chr(hexdec(substr($hex, 2, 2))) .chr(hexdec(substr($hex, 0, 2))) .chr(0) .chr(0); |
---|
1893 | |
---|
1894 | $key = $this->md5_16($tmp); |
---|
1895 | |
---|
1896 | $this->ARC4_init(substr($key, 0, 10)); |
---|
1897 | } |
---|
1898 | |
---|
1899 | |
---|
1900 | /** |
---|
1901 | * initialize the ARC4 encryption |
---|
1902 | */ |
---|
1903 | function ARC4_init($key = '') { |
---|
1904 | |
---|
1905 | $this->arc4 = ''; |
---|
1906 | |
---|
1907 | // setup the control array |
---|
1908 | if (strlen($key) == 0) { |
---|
1909 | |
---|
1910 | return; |
---|
1911 | } |
---|
1912 | |
---|
1913 | $k = ''; |
---|
1914 | |
---|
1915 | while (strlen($k) <256) { |
---|
1916 | |
---|
1917 | $k.= $key; |
---|
1918 | } |
---|
1919 | |
---|
1920 | $k = substr($k, 0, 256); |
---|
1921 | |
---|
1922 | for ($i = 0;$i<256;$i++) { |
---|
1923 | |
---|
1924 | $this->arc4.= chr($i); |
---|
1925 | } |
---|
1926 | |
---|
1927 | $j = 0; |
---|
1928 | |
---|
1929 | for ($i = 0;$i<256;$i++) { |
---|
1930 | |
---|
1931 | $t = $this->arc4[$i]; |
---|
1932 | |
---|
1933 | $j = ($j + ord($t) + ord($k[$i])) %256; |
---|
1934 | |
---|
1935 | $this->arc4[$i] = $this->arc4[$j]; |
---|
1936 | |
---|
1937 | $this->arc4[$j] = $t; |
---|
1938 | } |
---|
1939 | } |
---|
1940 | |
---|
1941 | |
---|
1942 | /** |
---|
1943 | * ARC4 encrypt a text string |
---|
1944 | */ |
---|
1945 | function ARC4($text) { |
---|
1946 | |
---|
1947 | $len = strlen($text); |
---|
1948 | |
---|
1949 | $a = 0; |
---|
1950 | |
---|
1951 | $b = 0; |
---|
1952 | |
---|
1953 | $c = $this->arc4; |
---|
1954 | |
---|
1955 | $out = ''; |
---|
1956 | |
---|
1957 | for ($i = 0;$i<$len;$i++) { |
---|
1958 | |
---|
1959 | $a = ($a+1) %256; |
---|
1960 | |
---|
1961 | $t = $c[$a]; |
---|
1962 | |
---|
1963 | $b = ($b+ord($t)) %256; |
---|
1964 | |
---|
1965 | $c[$a] = $c[$b]; |
---|
1966 | |
---|
1967 | $c[$b] = $t; |
---|
1968 | |
---|
1969 | $k = ord($c[(ord($c[$a]) +ord($c[$b])) %256]); |
---|
1970 | |
---|
1971 | $out.= chr(ord($text[$i]) ^ $k); |
---|
1972 | } |
---|
1973 | |
---|
1974 | |
---|
1975 | return $out; |
---|
1976 | } |
---|
1977 | |
---|
1978 | |
---|
1979 | /** |
---|
1980 | * functions which can be called to adjust or add to the document |
---|
1981 | */ |
---|
1982 | |
---|
1983 | /** |
---|
1984 | * add a link in the document to an external URL |
---|
1985 | */ |
---|
1986 | function addLink($url, $x0, $y0, $x1, $y1) { |
---|
1987 | |
---|
1988 | $this->numObj++; |
---|
1989 | |
---|
1990 | $info = array('type'=>'link', 'url'=>$url, 'rect'=>array($x0, $y0, $x1, $y1)); |
---|
1991 | |
---|
1992 | $this->o_annotation($this->numObj, 'new', $info); |
---|
1993 | } |
---|
1994 | |
---|
1995 | |
---|
1996 | /** |
---|
1997 | * add a link in the document to an internal destination (ie. within the document) |
---|
1998 | */ |
---|
1999 | function addInternalLink($label, $x0, $y0, $x1, $y1) { |
---|
2000 | |
---|
2001 | $this->numObj++; |
---|
2002 | |
---|
2003 | $info = array('type'=>'ilink', 'label'=>$label, 'rect'=>array($x0, $y0, $x1, $y1)); |
---|
2004 | |
---|
2005 | $this->o_annotation($this->numObj, 'new', $info); |
---|
2006 | } |
---|
2007 | |
---|
2008 | |
---|
2009 | /** |
---|
2010 | * set the encryption of the document |
---|
2011 | * can be used to turn it on and/or set the passwords which it will have. |
---|
2012 | * also the functions that the user will have are set here, such as print, modify, add |
---|
2013 | */ |
---|
2014 | function setEncryption($userPass = '', $ownerPass = '', $pc = array()) { |
---|
2015 | |
---|
2016 | $p = bindec(11000000); |
---|
2017 | |
---|
2018 | |
---|
2019 | $options = array( |
---|
2020 | 'print'=>4, 'modify'=>8, 'copy'=>16, 'add'=>32); |
---|
2021 | |
---|
2022 | foreach($pc as $k=>$v) { |
---|
2023 | |
---|
2024 | if ($v && isset($options[$k])) { |
---|
2025 | |
---|
2026 | $p+= $options[$k]; |
---|
2027 | } else if (isset($options[$v])) { |
---|
2028 | |
---|
2029 | $p+= $options[$v]; |
---|
2030 | } |
---|
2031 | } |
---|
2032 | |
---|
2033 | // implement encryption on the document |
---|
2034 | if ($this->arc4_objnum == 0) { |
---|
2035 | |
---|
2036 | // then the block does not exist already, add it. |
---|
2037 | $this->numObj++; |
---|
2038 | |
---|
2039 | if (strlen($ownerPass) == 0) { |
---|
2040 | |
---|
2041 | $ownerPass = $userPass; |
---|
2042 | } |
---|
2043 | |
---|
2044 | $this->o_encryption($this->numObj, 'new', array('user'=>$userPass, 'owner'=>$ownerPass, 'p'=>$p)); |
---|
2045 | } |
---|
2046 | } |
---|
2047 | |
---|
2048 | |
---|
2049 | /** |
---|
2050 | * should be used for internal checks, not implemented as yet |
---|
2051 | */ |
---|
2052 | function checkAllHere() { |
---|
2053 | } |
---|
2054 | |
---|
2055 | |
---|
2056 | /** |
---|
2057 | * return the pdf stream as a string returned from the function |
---|
2058 | */ |
---|
2059 | function output($debug = 0) { |
---|
2060 | |
---|
2061 | |
---|
2062 | if ($debug) { |
---|
2063 | |
---|
2064 | // turn compression off |
---|
2065 | $this->options['compression'] = 0; |
---|
2066 | } |
---|
2067 | |
---|
2068 | |
---|
2069 | if ($this->arc4_objnum) { |
---|
2070 | |
---|
2071 | $this->ARC4_init($this->encryptionKey); |
---|
2072 | } |
---|
2073 | |
---|
2074 | |
---|
2075 | $this->checkAllHere(); |
---|
2076 | |
---|
2077 | |
---|
2078 | $xref = array(); |
---|
2079 | |
---|
2080 | $content = "%PDF-1.3\n%âãÏÓ\n"; |
---|
2081 | |
---|
2082 | // $content="%PDF-1.3\n"; |
---|
2083 | $pos = strlen($content); |
---|
2084 | |
---|
2085 | foreach($this->objects as $k=>$v) { |
---|
2086 | |
---|
2087 | $tmp = 'o_'.$v['t']; |
---|
2088 | |
---|
2089 | $cont = $this->$tmp($k, 'out'); |
---|
2090 | |
---|
2091 | $content.= $cont; |
---|
2092 | |
---|
2093 | $xref[] = $pos; |
---|
2094 | |
---|
2095 | $pos+= strlen($cont); |
---|
2096 | } |
---|
2097 | |
---|
2098 | $content.= "\nxref\n0 ".(count($xref) +1) ."\n0000000000 65535 f \n"; |
---|
2099 | |
---|
2100 | foreach($xref as $p) { |
---|
2101 | |
---|
2102 | $content.= str_pad($p, 10, "0", STR_PAD_LEFT) . " 00000 n \n"; |
---|
2103 | } |
---|
2104 | |
---|
2105 | $content.= "trailer\n<<\n/Size ".(count($xref) +1) ."\n/Root 1 0 R\n/Info ".$this->infoObject." 0 R\n"; |
---|
2106 | |
---|
2107 | // if encryption has been applied to this document then add the marker for this dictionary |
---|
2108 | if ($this->arc4_objnum > 0) { |
---|
2109 | |
---|
2110 | $content.= "/Encrypt ".$this->arc4_objnum." 0 R\n"; |
---|
2111 | } |
---|
2112 | |
---|
2113 | if (strlen($this->fileIdentifier)) { |
---|
2114 | |
---|
2115 | $content.= "/ID[<".$this->fileIdentifier."><".$this->fileIdentifier.">]\n"; |
---|
2116 | } |
---|
2117 | |
---|
2118 | $content.= ">>\nstartxref\n".$pos."\n%%EOF\n"; |
---|
2119 | |
---|
2120 | return $content; |
---|
2121 | } |
---|
2122 | |
---|
2123 | |
---|
2124 | /** |
---|
2125 | * intialize a new document |
---|
2126 | * if this is called on an existing document results may be unpredictable, but the existing document would be lost at minimum |
---|
2127 | * this function is called automatically by the constructor function |
---|
2128 | * |
---|
2129 | * @access private |
---|
2130 | */ |
---|
2131 | function newDocument($pageSize = array(0, 0, 612, 792)) { |
---|
2132 | |
---|
2133 | $this->numObj = 0; |
---|
2134 | |
---|
2135 | $this->objects = array(); |
---|
2136 | |
---|
2137 | |
---|
2138 | $this->numObj++; |
---|
2139 | |
---|
2140 | $this->o_catalog($this->numObj, 'new'); |
---|
2141 | |
---|
2142 | |
---|
2143 | $this->numObj++; |
---|
2144 | |
---|
2145 | $this->o_outlines($this->numObj, 'new'); |
---|
2146 | |
---|
2147 | |
---|
2148 | $this->numObj++; |
---|
2149 | |
---|
2150 | $this->o_pages($this->numObj, 'new'); |
---|
2151 | |
---|
2152 | |
---|
2153 | $this->o_pages($this->numObj, 'mediaBox', $pageSize); |
---|
2154 | |
---|
2155 | $this->currentNode = 3; |
---|
2156 | |
---|
2157 | |
---|
2158 | $this->numObj++; |
---|
2159 | |
---|
2160 | $this->o_procset($this->numObj, 'new'); |
---|
2161 | |
---|
2162 | |
---|
2163 | $this->numObj++; |
---|
2164 | |
---|
2165 | $this->o_info($this->numObj, 'new'); |
---|
2166 | |
---|
2167 | |
---|
2168 | $this->numObj++; |
---|
2169 | |
---|
2170 | $this->o_page($this->numObj, 'new'); |
---|
2171 | |
---|
2172 | |
---|
2173 | // need to store the first page id as there is no way to get it to the user during |
---|
2174 | // startup |
---|
2175 | $this->firstPageId = $this->currentContents; |
---|
2176 | } |
---|
2177 | |
---|
2178 | |
---|
2179 | /** |
---|
2180 | * open the font file and return a php structure containing it. |
---|
2181 | * first check if this one has been done before and saved in a form more suited to php |
---|
2182 | * note that if a php serialized version does not exist it will try and make one, but will |
---|
2183 | * require write access to the directory to do it... it is MUCH faster to have these serialized |
---|
2184 | * files. |
---|
2185 | * |
---|
2186 | * @access private |
---|
2187 | */ |
---|
2188 | function openFont($font) { |
---|
2189 | |
---|
2190 | // assume that $font contains both the path and perhaps the extension to the file, split them |
---|
2191 | $pos = strrpos($font, '/'); |
---|
2192 | |
---|
2193 | if ($pos === false) { |
---|
2194 | |
---|
2195 | $dir = './'; |
---|
2196 | |
---|
2197 | $name = $font; |
---|
2198 | } else { |
---|
2199 | |
---|
2200 | $dir = substr($font, 0, $pos+1); |
---|
2201 | |
---|
2202 | $name = substr($font, $pos+1); |
---|
2203 | } |
---|
2204 | |
---|
2205 | |
---|
2206 | if (substr($name, -4) == '.afm') { |
---|
2207 | |
---|
2208 | $name = substr($name, 0, strlen($name) -4); |
---|
2209 | } |
---|
2210 | |
---|
2211 | $this->addMessage('openFont: '.$font.' - '.$name); |
---|
2212 | |
---|
2213 | if (file_exists($dir . 'php_' . $name . '.afm')) { |
---|
2214 | |
---|
2215 | $this->addMessage('openFont: php file exists ' . $dir . 'php_' . $name.'.afm'); |
---|
2216 | |
---|
2217 | $tmp = file_get_contents($dir.'php_'.$name.'.afm'); |
---|
2218 | |
---|
2219 | eval($tmp); |
---|
2220 | |
---|
2221 | if (!isset($this->fonts[$font]['_version_']) || $this->fonts[$font]['_version_']<1) { |
---|
2222 | |
---|
2223 | // if the font file is old, then clear it out and prepare for re-creation |
---|
2224 | $this->addMessage('openFont: clear out, make way for new version.'); |
---|
2225 | |
---|
2226 | unset($this->fonts[$font]); |
---|
2227 | } |
---|
2228 | } |
---|
2229 | |
---|
2230 | if (!isset($this->fonts[$font]) && file_exists($dir.$name.'.afm')) { |
---|
2231 | |
---|
2232 | // then rebuild the php_<font>.afm file from the <font>.afm file |
---|
2233 | $this->addMessage('openFont: build php file from '.$dir.$name.'.afm'); |
---|
2234 | |
---|
2235 | $data = array(); |
---|
2236 | |
---|
2237 | $file = file($dir.$name.'.afm'); |
---|
2238 | |
---|
2239 | foreach ($file as $rowA) { |
---|
2240 | |
---|
2241 | $row = trim($rowA); |
---|
2242 | |
---|
2243 | $pos = strpos($row, ' '); |
---|
2244 | |
---|
2245 | if ($pos) { |
---|
2246 | |
---|
2247 | // then there must be some keyword |
---|
2248 | $key = substr($row, 0, $pos); |
---|
2249 | |
---|
2250 | switch ($key) { |
---|
2251 | |
---|
2252 | case 'FontName': |
---|
2253 | |
---|
2254 | case 'FullName': |
---|
2255 | |
---|
2256 | case 'FamilyName': |
---|
2257 | |
---|
2258 | case 'Weight': |
---|
2259 | |
---|
2260 | case 'ItalicAngle': |
---|
2261 | |
---|
2262 | case 'IsFixedPitch': |
---|
2263 | |
---|
2264 | case 'CharacterSet': |
---|
2265 | |
---|
2266 | case 'UnderlinePosition': |
---|
2267 | |
---|
2268 | case 'UnderlineThickness': |
---|
2269 | |
---|
2270 | case 'Version': |
---|
2271 | |
---|
2272 | case 'EncodingScheme': |
---|
2273 | |
---|
2274 | case 'CapHeight': |
---|
2275 | |
---|
2276 | case 'XHeight': |
---|
2277 | |
---|
2278 | case 'Ascender': |
---|
2279 | |
---|
2280 | case 'Descender': |
---|
2281 | |
---|
2282 | case 'StdHW': |
---|
2283 | |
---|
2284 | case 'StdVW': |
---|
2285 | |
---|
2286 | case 'StartCharMetrics': |
---|
2287 | |
---|
2288 | $data[$key] = trim(substr($row, $pos)); |
---|
2289 | |
---|
2290 | break; |
---|
2291 | |
---|
2292 | case 'FontBBox': |
---|
2293 | |
---|
2294 | $data[$key] = explode(' ', trim(substr($row, $pos))); |
---|
2295 | |
---|
2296 | break; |
---|
2297 | |
---|
2298 | case 'C': |
---|
2299 | |
---|
2300 | //C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ; |
---|
2301 | $bits = explode(';', trim($row)); |
---|
2302 | |
---|
2303 | $dtmp = array(); |
---|
2304 | |
---|
2305 | foreach($bits as $bit) { |
---|
2306 | |
---|
2307 | $bits2 = explode(' ', trim($bit)); |
---|
2308 | |
---|
2309 | if (strlen($bits2[0])) { |
---|
2310 | |
---|
2311 | if (count($bits2) >2) { |
---|
2312 | |
---|
2313 | $dtmp[$bits2[0]] = array(); |
---|
2314 | |
---|
2315 | for ($i = 1;$i<count($bits2);$i++) { |
---|
2316 | |
---|
2317 | $dtmp[$bits2[0]][] = $bits2[$i]; |
---|
2318 | } |
---|
2319 | } else if (count($bits2) == 2) { |
---|
2320 | |
---|
2321 | $dtmp[$bits2[0]] = $bits2[1]; |
---|
2322 | } |
---|
2323 | } |
---|
2324 | } |
---|
2325 | |
---|
2326 | if ($dtmp['C'] >= 0) { |
---|
2327 | |
---|
2328 | $data['C'][$dtmp['C']] = $dtmp; |
---|
2329 | |
---|
2330 | $data['C'][$dtmp['N']] = $dtmp; |
---|
2331 | } else { |
---|
2332 | |
---|
2333 | $data['C'][$dtmp['N']] = $dtmp; |
---|
2334 | } |
---|
2335 | |
---|
2336 | break; |
---|
2337 | |
---|
2338 | case 'KPX': |
---|
2339 | |
---|
2340 | //KPX Adieresis yacute -40 |
---|
2341 | $bits = explode(' ', trim($row)); |
---|
2342 | |
---|
2343 | $data['KPX'][$bits[1]][$bits[2]] = $bits[3]; |
---|
2344 | |
---|
2345 | break; |
---|
2346 | } |
---|
2347 | } |
---|
2348 | } |
---|
2349 | |
---|
2350 | $data['_version_'] = 1; |
---|
2351 | |
---|
2352 | $this->fonts[$font] = $data; |
---|
2353 | |
---|
2354 | file_put_contents($dir . 'php_' . $name . '.afm', '$this->fonts[$font]=' . var_export($data, true) . ';'); |
---|
2355 | } else if (!isset($this->fonts[$font])) { |
---|
2356 | |
---|
2357 | $this->addMessage('openFont: no font file found'); |
---|
2358 | |
---|
2359 | // echo 'Font not Found '.$font; |
---|
2360 | |
---|
2361 | } |
---|
2362 | } |
---|
2363 | |
---|
2364 | |
---|
2365 | /** |
---|
2366 | * if the font is not loaded then load it and make the required object |
---|
2367 | * else just make it the current font |
---|
2368 | * the encoding array can contain 'encoding'=> 'none','WinAnsiEncoding','MacRomanEncoding' or 'MacExpertEncoding' |
---|
2369 | * note that encoding='none' will need to be used for symbolic fonts |
---|
2370 | * and 'differences' => an array of mappings between numbers 0->255 and character names. |
---|
2371 | * |
---|
2372 | */ |
---|
2373 | function selectFont($fontName, $encoding = '', $set = 1) { |
---|
2374 | |
---|
2375 | if (!isset($this->fonts[$fontName])) { |
---|
2376 | |
---|
2377 | // load the file |
---|
2378 | $this->openFont($fontName); |
---|
2379 | |
---|
2380 | if (isset($this->fonts[$fontName])) { |
---|
2381 | |
---|
2382 | $this->numObj++; |
---|
2383 | |
---|
2384 | $this->numFonts++; |
---|
2385 | |
---|
2386 | //$this->numFonts = md5($fontName); |
---|
2387 | $pos = strrpos($fontName, '/'); |
---|
2388 | |
---|
2389 | // $dir = substr($fontName,0,$pos+1); |
---|
2390 | $name = substr($fontName, $pos+1); |
---|
2391 | |
---|
2392 | if (substr($name, -4) == '.afm') { |
---|
2393 | |
---|
2394 | $name = substr($name, 0, strlen($name) -4); |
---|
2395 | } |
---|
2396 | |
---|
2397 | |
---|
2398 | $options = array('name' => $name); |
---|
2399 | |
---|
2400 | if (is_array($encoding)) { |
---|
2401 | |
---|
2402 | // then encoding and differences might be set |
---|
2403 | if (isset($encoding['encoding'])) { |
---|
2404 | |
---|
2405 | $options['encoding'] = $encoding['encoding']; |
---|
2406 | } |
---|
2407 | |
---|
2408 | if (isset($encoding['differences'])) { |
---|
2409 | |
---|
2410 | $options['differences'] = $encoding['differences']; |
---|
2411 | } |
---|
2412 | } else if (strlen($encoding)) { |
---|
2413 | |
---|
2414 | // then perhaps only the encoding has been set |
---|
2415 | $options['encoding'] = $encoding; |
---|
2416 | } |
---|
2417 | |
---|
2418 | |
---|
2419 | $fontObj = $this->numObj; |
---|
2420 | |
---|
2421 | $this->o_font($this->numObj, 'new', $options); |
---|
2422 | |
---|
2423 | $this->fonts[$fontName]['fontNum'] = $this->numFonts; |
---|
2424 | |
---|
2425 | |
---|
2426 | // if this is a '.afm' font, and there is a '.pfa' file to go with it ( as there |
---|
2427 | // should be for all non-basic fonts), then load it into an object and put the |
---|
2428 | // references into the font object |
---|
2429 | $basefile = substr($fontName, 0, strlen($fontName) -4); |
---|
2430 | |
---|
2431 | if (file_exists($basefile.'.pfb')) { |
---|
2432 | |
---|
2433 | $fbtype = 'pfb'; |
---|
2434 | } else if (file_exists($basefile.'.ttf')) { |
---|
2435 | |
---|
2436 | $fbtype = 'ttf'; |
---|
2437 | } else { |
---|
2438 | |
---|
2439 | $fbtype = ''; |
---|
2440 | } |
---|
2441 | |
---|
2442 | $fbfile = $basefile.'.'.$fbtype; |
---|
2443 | |
---|
2444 | |
---|
2445 | // $pfbfile = substr($fontName,0,strlen($fontName)-4).'.pfb'; |
---|
2446 | // $ttffile = substr($fontName,0,strlen($fontName)-4).'.ttf'; |
---|
2447 | $this->addMessage('selectFont: checking for - '.$fbfile); |
---|
2448 | |
---|
2449 | |
---|
2450 | if (substr($fontName, -4) == '.afm' && strlen($fbtype)) { |
---|
2451 | |
---|
2452 | $adobeFontName = $this->fonts[$fontName]['FontName']; |
---|
2453 | |
---|
2454 | // $fontObj = $this->numObj; |
---|
2455 | $this->addMessage('selectFont: adding font file - '.$fbfile.' - '.$adobeFontName); |
---|
2456 | |
---|
2457 | // find the array of fond widths, and put that into an object. |
---|
2458 | $firstChar = -1; |
---|
2459 | |
---|
2460 | $lastChar = 0; |
---|
2461 | |
---|
2462 | $widths = array(); |
---|
2463 | |
---|
2464 | foreach ($this->fonts[$fontName]['C'] as $num => $d) { |
---|
2465 | |
---|
2466 | if (intval($num) >0 || $num == '0') { |
---|
2467 | |
---|
2468 | if ($lastChar>0 && $num>$lastChar+1) { |
---|
2469 | |
---|
2470 | for ($i = $lastChar+1;$i<$num;$i++) { |
---|
2471 | |
---|
2472 | $widths[] = 0; |
---|
2473 | } |
---|
2474 | } |
---|
2475 | |
---|
2476 | $widths[] = $d['WX']; |
---|
2477 | |
---|
2478 | if ($firstChar == -1) { |
---|
2479 | |
---|
2480 | $firstChar = $num; |
---|
2481 | } |
---|
2482 | |
---|
2483 | $lastChar = $num; |
---|
2484 | } |
---|
2485 | } |
---|
2486 | |
---|
2487 | // also need to adjust the widths for the differences array |
---|
2488 | if (isset($options['differences'])) { |
---|
2489 | |
---|
2490 | foreach($options['differences'] as $charNum => $charName) { |
---|
2491 | |
---|
2492 | if ($charNum > $lastChar) { |
---|
2493 | |
---|
2494 | for ($i = $lastChar + 1; $i <= $charNum; $i++) { |
---|
2495 | |
---|
2496 | $widths[] = 0; |
---|
2497 | } |
---|
2498 | |
---|
2499 | $lastChar = $charNum; |
---|
2500 | } |
---|
2501 | |
---|
2502 | if (isset($this->fonts[$fontName]['C'][$charName])) { |
---|
2503 | |
---|
2504 | $widths[$charNum-$firstChar] = $this->fonts[$fontName]['C'][$charName]['WX']; |
---|
2505 | } |
---|
2506 | } |
---|
2507 | } |
---|
2508 | |
---|
2509 | $this->addMessage('selectFont: FirstChar = '.$firstChar); |
---|
2510 | |
---|
2511 | $this->addMessage('selectFont: LastChar = '.$lastChar); |
---|
2512 | |
---|
2513 | $this->numObj++; |
---|
2514 | |
---|
2515 | $this->o_contents($this->numObj, 'new', 'raw'); |
---|
2516 | |
---|
2517 | $this->objects[$this->numObj]['c'].= '['; |
---|
2518 | |
---|
2519 | foreach($widths as $width) { |
---|
2520 | |
---|
2521 | $this->objects[$this->numObj]['c'].= ' '.$width; |
---|
2522 | } |
---|
2523 | |
---|
2524 | $this->objects[$this->numObj]['c'].= ' ]'; |
---|
2525 | |
---|
2526 | $widthid = $this->numObj; |
---|
2527 | |
---|
2528 | |
---|
2529 | // load the pfb file, and put that into an object too. |
---|
2530 | // note that pdf supports only binary format type 1 font files, though there is a |
---|
2531 | // simple utility to convert them from pfa to pfb. |
---|
2532 | $tmp = get_magic_quotes_runtime(); |
---|
2533 | |
---|
2534 | set_magic_quotes_runtime(0); |
---|
2535 | |
---|
2536 | $data = file_get_contents($fbfile); |
---|
2537 | |
---|
2538 | set_magic_quotes_runtime($tmp); |
---|
2539 | |
---|
2540 | |
---|
2541 | // create the font descriptor |
---|
2542 | $this->numObj++; |
---|
2543 | |
---|
2544 | $fontDescriptorId = $this->numObj; |
---|
2545 | |
---|
2546 | $this->numObj++; |
---|
2547 | |
---|
2548 | $pfbid = $this->numObj; |
---|
2549 | |
---|
2550 | // determine flags (more than a little flakey, hopefully will not matter much) |
---|
2551 | $flags = 0; |
---|
2552 | |
---|
2553 | if ($this->fonts[$fontName]['ItalicAngle'] != 0) { |
---|
2554 | $flags+= pow(2, 6); |
---|
2555 | } |
---|
2556 | |
---|
2557 | if ($this->fonts[$fontName]['IsFixedPitch'] == 'true') { |
---|
2558 | $flags+= 1; |
---|
2559 | } |
---|
2560 | |
---|
2561 | $flags+= pow(2, 5); |
---|
2562 | // assume non-sybolic |
---|
2563 | |
---|
2564 | $list = array('Ascent' => 'Ascender', 'CapHeight' => 'CapHeight', 'Descent' => 'Descender', 'FontBBox' => 'FontBBox', 'ItalicAngle' => 'ItalicAngle'); |
---|
2565 | |
---|
2566 | $fdopt = array( |
---|
2567 | 'Flags' => $flags, 'FontName' => $adobeFontName, 'StemV' => 100 // don't know what the value for this should be! |
---|
2568 | ); |
---|
2569 | |
---|
2570 | foreach($list as $k => $v) { |
---|
2571 | |
---|
2572 | if (isset($this->fonts[$fontName][$v])) { |
---|
2573 | |
---|
2574 | $fdopt[$k] = $this->fonts[$fontName][$v]; |
---|
2575 | } |
---|
2576 | } |
---|
2577 | |
---|
2578 | |
---|
2579 | if ($fbtype == 'pfb') { |
---|
2580 | |
---|
2581 | $fdopt['FontFile'] = $pfbid; |
---|
2582 | } else if ($fbtype == 'ttf') { |
---|
2583 | |
---|
2584 | $fdopt['FontFile2'] = $pfbid; |
---|
2585 | } |
---|
2586 | |
---|
2587 | $this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt); |
---|
2588 | |
---|
2589 | |
---|
2590 | // embed the font program |
---|
2591 | $this->o_contents($this->numObj, 'new'); |
---|
2592 | |
---|
2593 | $this->objects[$pfbid]['c'].= $data; |
---|
2594 | |
---|
2595 | // determine the cruicial lengths within this file |
---|
2596 | if ($fbtype == 'pfb') { |
---|
2597 | |
---|
2598 | $l1 = strpos($data, 'eexec') +6; |
---|
2599 | |
---|
2600 | $l2 = strpos($data, '00000000') -$l1; |
---|
2601 | |
---|
2602 | $l3 = strlen($data) -$l2-$l1; |
---|
2603 | |
---|
2604 | $this->o_contents($this->numObj, 'add', array('Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3)); |
---|
2605 | } else if ($fbtype == 'ttf') { |
---|
2606 | |
---|
2607 | $l1 = strlen($data); |
---|
2608 | |
---|
2609 | $this->o_contents($this->numObj, 'add', array('Length1' => $l1)); |
---|
2610 | } |
---|
2611 | |
---|
2612 | |
---|
2613 | |
---|
2614 | // tell the font object about all this new stuff |
---|
2615 | $tmp = array('BaseFont' => $adobeFontName, 'Widths' => $widthid, 'FirstChar' => $firstChar, 'LastChar' => $lastChar, 'FontDescriptor' => $fontDescriptorId); |
---|
2616 | |
---|
2617 | if ($fbtype == 'ttf') { |
---|
2618 | |
---|
2619 | $tmp['SubType'] = 'TrueType'; |
---|
2620 | } |
---|
2621 | |
---|
2622 | $this->addMessage('adding extra info to font.('.$fontObj.')'); |
---|
2623 | |
---|
2624 | foreach($tmp as $fk => $fv) { |
---|
2625 | |
---|
2626 | $this->addMessage($fk." : ".$fv); |
---|
2627 | } |
---|
2628 | |
---|
2629 | $this->o_font($fontObj, 'add', $tmp); |
---|
2630 | } else { |
---|
2631 | |
---|
2632 | $this->addMessage('selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts'); |
---|
2633 | } |
---|
2634 | |
---|
2635 | |
---|
2636 | |
---|
2637 | // also set the differences here, note that this means that these will take effect only the |
---|
2638 | //first time that a font is selected, else they are ignored |
---|
2639 | if (isset($options['differences'])) { |
---|
2640 | |
---|
2641 | $this->fonts[$fontName]['differences'] = $options['differences']; |
---|
2642 | } |
---|
2643 | } |
---|
2644 | } |
---|
2645 | |
---|
2646 | if ($set && isset($this->fonts[$fontName])) { |
---|
2647 | |
---|
2648 | // so if for some reason the font was not set in the last one then it will not be selected |
---|
2649 | $this->currentBaseFont = $fontName; |
---|
2650 | |
---|
2651 | // the next lines mean that if a new font is selected, then the current text state will be |
---|
2652 | // applied to it as well. |
---|
2653 | $this->currentFont = $this->currentBaseFont; |
---|
2654 | |
---|
2655 | $this->currentFontNum = $this->fonts[$this->currentFont]['fontNum']; |
---|
2656 | |
---|
2657 | //$this->setCurrentFont(); |
---|
2658 | |
---|
2659 | } |
---|
2660 | |
---|
2661 | return $this->currentFontNum; |
---|
2662 | |
---|
2663 | //return $this->numObj; |
---|
2664 | |
---|
2665 | } |
---|
2666 | |
---|
2667 | |
---|
2668 | /** |
---|
2669 | * sets up the current font, based on the font families, and the current text state |
---|
2670 | * note that this system is quite flexible, a bold-italic font can be completely different to a |
---|
2671 | * italic-bold font, and even bold-bold will have to be defined within the family to have meaning |
---|
2672 | * This function is to be called whenever the currentTextState is changed, it will update |
---|
2673 | * the currentFont setting to whatever the appropriatte family one is. |
---|
2674 | * If the user calls selectFont themselves then that will reset the currentBaseFont, and the currentFont |
---|
2675 | * This function will change the currentFont to whatever it should be, but will not change the |
---|
2676 | * currentBaseFont. |
---|
2677 | * |
---|
2678 | * @access private |
---|
2679 | */ |
---|
2680 | function setCurrentFont() { |
---|
2681 | |
---|
2682 | // if (strlen($this->currentBaseFont) == 0){ |
---|
2683 | // // then assume an initial font |
---|
2684 | // $this->selectFont('./fonts/Helvetica.afm'); |
---|
2685 | // } |
---|
2686 | // $cf = substr($this->currentBaseFont,strrpos($this->currentBaseFont,'/')+1); |
---|
2687 | // if (strlen($this->currentTextState) |
---|
2688 | // && isset($this->fontFamilies[$cf]) |
---|
2689 | // && isset($this->fontFamilies[$cf][$this->currentTextState])){ |
---|
2690 | // // then we are in some state or another |
---|
2691 | // // and this font has a family, and the current setting exists within it |
---|
2692 | // // select the font, then return it |
---|
2693 | // $nf = substr($this->currentBaseFont,0,strrpos($this->currentBaseFont,'/')+1).$this->fontFamilies[$cf][$this->currentTextState]; |
---|
2694 | // $this->selectFont($nf,'',0); |
---|
2695 | // $this->currentFont = $nf; |
---|
2696 | // $this->currentFontNum = $this->fonts[$nf]['fontNum']; |
---|
2697 | // } else { |
---|
2698 | // // the this font must not have the right family member for the current state |
---|
2699 | // // simply assume the base font |
---|
2700 | $this->currentFont = $this->currentBaseFont; |
---|
2701 | |
---|
2702 | $this->currentFontNum = $this->fonts[$this->currentFont]['fontNum']; |
---|
2703 | |
---|
2704 | // } |
---|
2705 | |
---|
2706 | } |
---|
2707 | |
---|
2708 | |
---|
2709 | /** |
---|
2710 | * function for the user to find out what the ID is of the first page that was created during |
---|
2711 | * startup - useful if they wish to add something to it later. |
---|
2712 | */ |
---|
2713 | function getFirstPageId() { |
---|
2714 | |
---|
2715 | return $this->firstPageId; |
---|
2716 | } |
---|
2717 | |
---|
2718 | |
---|
2719 | /** |
---|
2720 | * add content to the currently active object |
---|
2721 | * |
---|
2722 | * @access private |
---|
2723 | */ |
---|
2724 | function addContent($content) { |
---|
2725 | |
---|
2726 | $this->objects[$this->currentContents]['c'].= $content; |
---|
2727 | } |
---|
2728 | |
---|
2729 | |
---|
2730 | /** |
---|
2731 | * sets the colour for fill operations |
---|
2732 | */ |
---|
2733 | function setColor($r, $g, $b, $force = 0) { |
---|
2734 | |
---|
2735 | if ($r >= 0 && ($force || $r != $this->currentColour['r'] || $g != $this->currentColour['g'] || $b != $this->currentColour['b'])) { |
---|
2736 | |
---|
2737 | $this->objects[$this->currentContents]['c'].= "\n".sprintf('%.3f', $r) .' '.sprintf('%.3f', $g) .' '.sprintf('%.3f', $b) .' rg'; |
---|
2738 | |
---|
2739 | $this->currentColour = array('r' => $r, 'g' => $g, 'b' => $b); |
---|
2740 | } |
---|
2741 | } |
---|
2742 | |
---|
2743 | |
---|
2744 | /** |
---|
2745 | * sets the colour for stroke operations |
---|
2746 | */ |
---|
2747 | function setStrokeColor($r, $g, $b, $force = 0) { |
---|
2748 | |
---|
2749 | if ($r >= 0 && ($force || $r != $this->currentStrokeColour['r'] || $g != $this->currentStrokeColour['g'] || $b != $this->currentStrokeColour['b'])) { |
---|
2750 | |
---|
2751 | $this->objects[$this->currentContents]['c'].= "\n".sprintf('%.3f', $r) .' '.sprintf('%.3f', $g) .' '.sprintf('%.3f', $b) .' RG'; |
---|
2752 | |
---|
2753 | $this->currentStrokeColour = array('r' => $r, 'g' => $g, 'b' => $b); |
---|
2754 | } |
---|
2755 | } |
---|
2756 | |
---|
2757 | |
---|
2758 | /** |
---|
2759 | * Set the graphics state for compositions |
---|
2760 | */ |
---|
2761 | function setGraphicsState($parameters) { |
---|
2762 | |
---|
2763 | // Create a new graphics state object |
---|
2764 | // FIXME: should actually keep track of states that have already been created... |
---|
2765 | $this->numObj++; |
---|
2766 | |
---|
2767 | $this->o_extGState($this->numObj, 'new', $parameters); |
---|
2768 | |
---|
2769 | $this->objects[ $this->currentContents ]['c'].= "\n/GS" . $this->numStates . " gs"; |
---|
2770 | } |
---|
2771 | |
---|
2772 | |
---|
2773 | /** |
---|
2774 | * Set current blend mode & opacity for lines. |
---|
2775 | * |
---|
2776 | * Valid blend modes are: |
---|
2777 | * |
---|
2778 | * Normal, Multiply, Screen, Overlay, Darken, Lighten, |
---|
2779 | * ColorDogde, ColorBurn, HardLight, SoftLight, Difference, |
---|
2780 | * Exclusion |
---|
2781 | * |
---|
2782 | * @param string $mode the blend mode to use |
---|
2783 | * @param float $opacity 0.0 fully transparent, 1.0 fully opaque |
---|
2784 | */ |
---|
2785 | function setLineTransparency($mode, $opacity) { |
---|
2786 | static $blend_modes = array("Normal", "Multiply", "Screen", |
---|
2787 | "Overlay", "Darken", "Lighten", |
---|
2788 | "ColorDogde", "ColorBurn", "HardLight", |
---|
2789 | "SoftLight", "Difference", "Exclusion"); |
---|
2790 | |
---|
2791 | if ( !in_array($mode, $blend_modes) ) |
---|
2792 | $mode = "Normal"; |
---|
2793 | |
---|
2794 | // Only create a new graphics state if required |
---|
2795 | if ( $mode == $this->currentLineTransparency["mode"] && |
---|
2796 | $opacity == $this->currentLineTransparency["opacity"] ) |
---|
2797 | return; |
---|
2798 | |
---|
2799 | $options = array("BM" => "/$mode", |
---|
2800 | "CA" => (float)$opacity); |
---|
2801 | |
---|
2802 | $this->setGraphicsState($options); |
---|
2803 | } |
---|
2804 | |
---|
2805 | /** |
---|
2806 | * Set current blend mode & opacity for filled objects. |
---|
2807 | * |
---|
2808 | * Valid blend modes are: |
---|
2809 | * |
---|
2810 | * Normal, Multiply, Screen, Overlay, Darken, Lighten, |
---|
2811 | * ColorDogde, ColorBurn, HardLight, SoftLight, Difference, |
---|
2812 | * Exclusion |
---|
2813 | * |
---|
2814 | * @param string $mode the blend mode to use |
---|
2815 | * @param float $opacity 0.0 fully transparent, 1.0 fully opaque |
---|
2816 | */ |
---|
2817 | function setFillTransparency($mode, $opacity) { |
---|
2818 | static $blend_modes = array("Normal", "Multiply", "Screen", |
---|
2819 | "Overlay", "Darken", "Lighten", |
---|
2820 | "ColorDogde", "ColorBurn", "HardLight", |
---|
2821 | "SoftLight", "Difference", "Exclusion"); |
---|
2822 | |
---|
2823 | if ( !in_array($mode, $blend_modes) ) |
---|
2824 | $mode = "Normal"; |
---|
2825 | |
---|
2826 | if ( $mode == $this->currentFillTransparency["mode"] && |
---|
2827 | $opacity == $this->currentFillTransparency["opacity"] ) |
---|
2828 | return; |
---|
2829 | |
---|
2830 | $options = array("BM" => "/$mode", |
---|
2831 | "ca" => (float)$opacity); |
---|
2832 | |
---|
2833 | $this->setGraphicsState($options); |
---|
2834 | } |
---|
2835 | |
---|
2836 | /** |
---|
2837 | * draw a line from one set of coordinates to another |
---|
2838 | */ |
---|
2839 | function line($x1, $y1, $x2, $y2) { |
---|
2840 | |
---|
2841 | $this->objects[$this->currentContents]['c'] .= |
---|
2842 | "\n".sprintf('%.3f', $x1) .' '.sprintf('%.3f', $y1) .' m '.sprintf('%.3f', $x2) .' '.sprintf('%.3f', $y2) .' l S'; |
---|
2843 | } |
---|
2844 | |
---|
2845 | |
---|
2846 | /** |
---|
2847 | * draw a bezier curve based on 4 control points |
---|
2848 | */ |
---|
2849 | function curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3) { |
---|
2850 | |
---|
2851 | // in the current line style, draw a bezier curve from (x0,y0) to (x3,y3) using the other two points |
---|
2852 | // as the control points for the curve. |
---|
2853 | $this->objects[$this->currentContents]['c'] .= |
---|
2854 | "\n".sprintf('%.3f', $x0) .' '.sprintf('%.3f', $y0) .' m '.sprintf('%.3f', $x1) .' '.sprintf('%.3f', $y1); |
---|
2855 | |
---|
2856 | $this->objects[$this->currentContents]['c'] .= |
---|
2857 | ' '.sprintf('%.3f', $x2) .' '.sprintf('%.3f', $y2) .' '.sprintf('%.3f', $x3) .' '.sprintf('%.3f', $y3) .' c S'; |
---|
2858 | } |
---|
2859 | |
---|
2860 | |
---|
2861 | /** |
---|
2862 | * draw a part of an ellipse |
---|
2863 | */ |
---|
2864 | function partEllipse($x0, $y0, $astart, $afinish, $r1, $r2 = 0, $angle = 0, $nSeg = 8) { |
---|
2865 | |
---|
2866 | $this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, 0); |
---|
2867 | } |
---|
2868 | |
---|
2869 | |
---|
2870 | /** |
---|
2871 | * draw a filled ellipse |
---|
2872 | */ |
---|
2873 | function filledEllipse($x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360) { |
---|
2874 | |
---|
2875 | return $this->ellipse($x0, $y0, $r1, $r2 = 0, $angle, $nSeg, $astart, $afinish, 1, 1); |
---|
2876 | } |
---|
2877 | |
---|
2878 | |
---|
2879 | /** |
---|
2880 | * draw an ellipse |
---|
2881 | * note that the part and filled ellipse are just special cases of this function |
---|
2882 | * |
---|
2883 | * draws an ellipse in the current line style |
---|
2884 | * centered at $x0,$y0, radii $r1,$r2 |
---|
2885 | * if $r2 is not set, then a circle is drawn |
---|
2886 | * nSeg is not allowed to be less than 2, as this will simply draw a line (and will even draw a |
---|
2887 | * pretty crappy shape at 2, as we are approximating with bezier curves. |
---|
2888 | */ |
---|
2889 | function ellipse($x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360, $close = 1, $fill = 0) { |
---|
2890 | |
---|
2891 | if ($r1 == 0) { |
---|
2892 | return; |
---|
2893 | } |
---|
2894 | |
---|
2895 | if ($r2 == 0) { |
---|
2896 | $r2 = $r1; |
---|
2897 | } |
---|
2898 | |
---|
2899 | if ($nSeg < 2) { |
---|
2900 | $nSeg = 2; |
---|
2901 | } |
---|
2902 | |
---|
2903 | |
---|
2904 | $astart = deg2rad((float)$astart); |
---|
2905 | |
---|
2906 | $afinish = deg2rad((float)$afinish); |
---|
2907 | |
---|
2908 | $totalAngle = $afinish-$astart; |
---|
2909 | |
---|
2910 | |
---|
2911 | $dt = $totalAngle/$nSeg; |
---|
2912 | |
---|
2913 | $dtm = $dt/3; |
---|
2914 | |
---|
2915 | |
---|
2916 | if ($angle != 0) { |
---|
2917 | |
---|
2918 | $a = -1*deg2rad((float)$angle); |
---|
2919 | |
---|
2920 | $tmp = "\n q "; |
---|
2921 | $tmp .= sprintf('%.3f', cos($a)) .' '.sprintf('%.3f', (-1.0*sin($a))) .' '.sprintf('%.3f', sin($a)) .' '.sprintf('%.3f', cos($a)) .' '; |
---|
2922 | $tmp .= sprintf('%.3f', $x0) .' '.sprintf('%.3f', $y0) .' cm'; |
---|
2923 | |
---|
2924 | $this->objects[$this->currentContents]['c'].= $tmp; |
---|
2925 | |
---|
2926 | $x0 = 0; |
---|
2927 | $y0 = 0; |
---|
2928 | } |
---|
2929 | |
---|
2930 | |
---|
2931 | $t1 = $astart; |
---|
2932 | $a0 = $x0 + $r1*cos($t1); |
---|
2933 | $b0 = $y0 + $r2*sin($t1); |
---|
2934 | $c0 = -$r1 * sin($t1); |
---|
2935 | $d0 = $r2 * cos($t1); |
---|
2936 | |
---|
2937 | |
---|
2938 | $this->objects[$this->currentContents]['c'] .= "\n".sprintf('%.3f', $a0) .' '.sprintf('%.3f', $b0) .' m '; |
---|
2939 | |
---|
2940 | for ($i = 1; $i <= $nSeg; $i++) { |
---|
2941 | |
---|
2942 | // draw this bit of the total curve |
---|
2943 | $t1 = $i * $dt + $astart; |
---|
2944 | |
---|
2945 | $a1 = $x0 + $r1 * cos($t1); |
---|
2946 | |
---|
2947 | $b1 = $y0 + $r2 * sin($t1); |
---|
2948 | |
---|
2949 | $c1 = -$r1 * sin($t1); |
---|
2950 | |
---|
2951 | $d1 = $r2 * cos($t1); |
---|
2952 | |
---|
2953 | $this->objects[$this->currentContents]['c'] |
---|
2954 | .= "\n".sprintf('%.3f', ($a0+$c0*$dtm)) .' '.sprintf('%.3f', ($b0 + $d0 * $dtm)); |
---|
2955 | |
---|
2956 | $this->objects[$this->currentContents]['c'] .= |
---|
2957 | ' '.sprintf('%.3f', ($a1-$c1*$dtm)) .' '.sprintf('%.3f', ($b1-$d1*$dtm)) .' '.sprintf('%.3f', $a1) .' '.sprintf('%.3f', $b1) .' c'; |
---|
2958 | |
---|
2959 | $a0 = $a1; |
---|
2960 | |
---|
2961 | $b0 = $b1; |
---|
2962 | |
---|
2963 | $c0 = $c1; |
---|
2964 | |
---|
2965 | $d0 = $d1; |
---|
2966 | } |
---|
2967 | |
---|
2968 | if ($fill) { |
---|
2969 | $this->objects[$this->currentContents]['c'].= ' f'; |
---|
2970 | |
---|
2971 | } else if ($close) { |
---|
2972 | |
---|
2973 | $this->objects[$this->currentContents]['c'].= ' s'; |
---|
2974 | // small 's' signifies closing the path as well |
---|
2975 | |
---|
2976 | } else { |
---|
2977 | |
---|
2978 | $this->objects[$this->currentContents]['c'].= ' S'; |
---|
2979 | |
---|
2980 | } |
---|
2981 | |
---|
2982 | if ($angle != 0) { |
---|
2983 | $this->objects[$this->currentContents]['c'].= ' Q'; |
---|
2984 | } |
---|
2985 | |
---|
2986 | } |
---|
2987 | |
---|
2988 | |
---|
2989 | /** |
---|
2990 | * this sets the line drawing style. |
---|
2991 | * width, is the thickness of the line in user units |
---|
2992 | * cap is the type of cap to put on the line, values can be 'butt','round','square' |
---|
2993 | * where the diffference between 'square' and 'butt' is that 'square' projects a flat end past the |
---|
2994 | * end of the line. |
---|
2995 | * join can be 'miter', 'round', 'bevel' |
---|
2996 | * dash is an array which sets the dash pattern, is a series of length values, which are the lengths of the |
---|
2997 | * on and off dashes. |
---|
2998 | * (2) represents 2 on, 2 off, 2 on , 2 off ... |
---|
2999 | * (2,1) is 2 on, 1 off, 2 on, 1 off.. etc |
---|
3000 | * phase is a modifier on the dash pattern which is used to shift the point at which the pattern starts. |
---|
3001 | */ |
---|
3002 | function setLineStyle($width = 1, $cap = '', $join = '', $dash = '', $phase = 0) { |
---|
3003 | |
---|
3004 | |
---|
3005 | // this is quite inefficient in that it sets all the parameters whenever 1 is changed, but will fix another day |
---|
3006 | $string = ''; |
---|
3007 | |
---|
3008 | if ($width>0) { |
---|
3009 | |
---|
3010 | $string.= $width.' w'; |
---|
3011 | } |
---|
3012 | |
---|
3013 | $ca = array('butt' => 0, 'round' => 1, 'square' => 2); |
---|
3014 | |
---|
3015 | if (isset($ca[$cap])) { |
---|
3016 | |
---|
3017 | $string.= ' '.$ca[$cap].' J'; |
---|
3018 | } |
---|
3019 | |
---|
3020 | $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2); |
---|
3021 | |
---|
3022 | if (isset($ja[$join])) { |
---|
3023 | |
---|
3024 | $string.= ' '.$ja[$join].' j'; |
---|
3025 | } |
---|
3026 | |
---|
3027 | if (is_array($dash)) { |
---|
3028 | |
---|
3029 | $string.= ' ['; |
---|
3030 | |
---|
3031 | foreach ($dash as $len) { |
---|
3032 | |
---|
3033 | $string.= ' '.$len; |
---|
3034 | } |
---|
3035 | |
---|
3036 | $string.= ' ] '.$phase.' d'; |
---|
3037 | } |
---|
3038 | |
---|
3039 | $this->currentLineStyle = $string; |
---|
3040 | |
---|
3041 | $this->objects[$this->currentContents]['c'].= "\n".$string; |
---|
3042 | } |
---|
3043 | |
---|
3044 | |
---|
3045 | |
---|
3046 | /** |
---|
3047 | * draw a polygon, the syntax for this is similar to the GD polygon command |
---|
3048 | */ |
---|
3049 | function polygon($p, $np, $f = 0) { |
---|
3050 | |
---|
3051 | $this->objects[$this->currentContents]['c'].= "\n"; |
---|
3052 | |
---|
3053 | $this->objects[$this->currentContents]['c'].= sprintf('%.3f', $p[0]) .' '.sprintf('%.3f', $p[1]) .' m '; |
---|
3054 | |
---|
3055 | for ($i = 2; $i < $np * 2; $i = $i + 2) { |
---|
3056 | |
---|
3057 | $this->objects[$this->currentContents]['c'].= sprintf('%.3f', $p[$i]) .' '.sprintf('%.3f', $p[$i+1]) .' l '; |
---|
3058 | } |
---|
3059 | |
---|
3060 | if ($f == 1) { |
---|
3061 | |
---|
3062 | $this->objects[$this->currentContents]['c'].= ' f'; |
---|
3063 | } else { |
---|
3064 | |
---|
3065 | $this->objects[$this->currentContents]['c'].= ' S'; |
---|
3066 | } |
---|
3067 | } |
---|
3068 | |
---|
3069 | |
---|
3070 | /** |
---|
3071 | * a filled rectangle, note that it is the width and height of the rectangle which are the secondary paramaters, not |
---|
3072 | * the coordinates of the upper-right corner |
---|
3073 | */ |
---|
3074 | function filledRectangle($x1, $y1, $width, $height) { |
---|
3075 | |
---|
3076 | $this->objects[$this->currentContents]['c'].= "\n".sprintf('%.3f', $x1) .' '.sprintf('%.3f', $y1) .' '.sprintf('%.3f', $width) .' '.sprintf('%.3f', $height) .' re f'; |
---|
3077 | } |
---|
3078 | |
---|
3079 | |
---|
3080 | /** |
---|
3081 | * draw a rectangle, note that it is the width and height of the rectangle which are the secondary paramaters, not |
---|
3082 | * the coordinates of the upper-right corner |
---|
3083 | */ |
---|
3084 | function rectangle($x1, $y1, $width, $height) { |
---|
3085 | |
---|
3086 | $this->objects[$this->currentContents]['c'].= "\n".sprintf('%.3f', $x1) .' '.sprintf('%.3f', $y1) .' '.sprintf('%.3f', $width) .' '.sprintf('%.3f', $height) .' re S'; |
---|
3087 | } |
---|
3088 | |
---|
3089 | |
---|
3090 | /** |
---|
3091 | * add a new page to the document |
---|
3092 | * this also makes the new page the current active object |
---|
3093 | */ |
---|
3094 | function newPage($insert = 0, $id = 0, $pos = 'after') { |
---|
3095 | |
---|
3096 | |
---|
3097 | // if there is a state saved, then go up the stack closing them |
---|
3098 | // then on the new page, re-open them with the right setings |
---|
3099 | |
---|
3100 | if ($this->nStateStack) { |
---|
3101 | |
---|
3102 | for ($i = $this->nStateStack;$i >= 1;$i--) { |
---|
3103 | |
---|
3104 | $this->restoreState($i); |
---|
3105 | } |
---|
3106 | } |
---|
3107 | |
---|
3108 | |
---|
3109 | $this->numObj++; |
---|
3110 | |
---|
3111 | if ($insert) { |
---|
3112 | |
---|
3113 | // the id from the ezPdf class is the od of the contents of the page, not the page object itself |
---|
3114 | // query that object to find the parent |
---|
3115 | $rid = $this->objects[$id]['onPage']; |
---|
3116 | |
---|
3117 | $opt = array('rid' => $rid, 'pos' => $pos); |
---|
3118 | |
---|
3119 | $this->o_page($this->numObj, 'new', $opt); |
---|
3120 | } else { |
---|
3121 | |
---|
3122 | $this->o_page($this->numObj, 'new'); |
---|
3123 | } |
---|
3124 | |
---|
3125 | // if there is a stack saved, then put that onto the page |
---|
3126 | if ($this->nStateStack) { |
---|
3127 | |
---|
3128 | for ($i = 1;$i <= $this->nStateStack;$i++) { |
---|
3129 | |
---|
3130 | $this->saveState($i); |
---|
3131 | } |
---|
3132 | } |
---|
3133 | |
---|
3134 | // and if there has been a stroke or fill colour set, then transfer them |
---|
3135 | if ($this->currentColour['r'] >= 0) { |
---|
3136 | |
---|
3137 | $this->setColor($this->currentColour['r'], $this->currentColour['g'], $this->currentColour['b'], 1); |
---|
3138 | } |
---|
3139 | |
---|
3140 | if ($this->currentStrokeColour['r'] >= 0) { |
---|
3141 | |
---|
3142 | $this->setStrokeColor($this->currentStrokeColour['r'], $this->currentStrokeColour['g'], $this->currentStrokeColour['b'], 1); |
---|
3143 | } |
---|
3144 | |
---|
3145 | |
---|
3146 | // if there is a line style set, then put this in too |
---|
3147 | if (strlen($this->currentLineStyle)) { |
---|
3148 | |
---|
3149 | $this->objects[$this->currentContents]['c'].= "\n".$this->currentLineStyle; |
---|
3150 | } |
---|
3151 | |
---|
3152 | |
---|
3153 | // the call to the o_page object set currentContents to the present page, so this can be returned as the page id |
---|
3154 | return $this->currentContents; |
---|
3155 | } |
---|
3156 | |
---|
3157 | |
---|
3158 | /** |
---|
3159 | * output the pdf code, streaming it to the browser |
---|
3160 | * the relevant headers are set so that hopefully the browser will recognise it |
---|
3161 | */ |
---|
3162 | function stream($options = '') { |
---|
3163 | |
---|
3164 | // setting the options allows the adjustment of the headers |
---|
3165 | // values at the moment are: |
---|
3166 | // 'Content-Disposition' => 'filename' - sets the filename, though not too sure how well this will |
---|
3167 | // work as in my trial the browser seems to use the filename of the php file with .pdf on the end |
---|
3168 | // 'Accept-Ranges' => 1 or 0 - if this is not set to 1, then this header is not included, off by default |
---|
3169 | // this header seems to have caused some problems despite tha fact that it is supposed to solve |
---|
3170 | // them, so I am leaving it off by default. |
---|
3171 | // 'compress' = > 1 or 0 - apply content stream compression, this is on (1) by default |
---|
3172 | // 'Attachment' => 1 or 0 - if 1, force the browser to open a download dialog |
---|
3173 | if (!is_array($options)) { |
---|
3174 | |
---|
3175 | $options = array(); |
---|
3176 | } |
---|
3177 | |
---|
3178 | if ( headers_sent()) |
---|
3179 | die("Unable to stream pdf: headers already sent"); |
---|
3180 | |
---|
3181 | |
---|
3182 | if ( isset($options['compress']) && $options['compress'] == 0) { |
---|
3183 | |
---|
3184 | $tmp = ltrim($this->output(1)); |
---|
3185 | } else { |
---|
3186 | |
---|
3187 | $tmp = ltrim($this->output()); |
---|
3188 | } |
---|
3189 | |
---|
3190 | |
---|
3191 | header("Cache-Control: private"); |
---|
3192 | |
---|
3193 | header("Content-type: application/pdf"); |
---|
3194 | |
---|
3195 | //header("Content-Length: " . strlen($tmp)); |
---|
3196 | $fileName = (isset($options['Content-Disposition']) ? $options['Content-Disposition'] : 'file.pdf'); |
---|
3197 | |
---|
3198 | if ( !isset($options["Attachment"])) |
---|
3199 | $options["Attachment"] = true; |
---|
3200 | |
---|
3201 | |
---|
3202 | $attachment = $options["Attachment"] ? "attachment" : "inline"; |
---|
3203 | |
---|
3204 | |
---|
3205 | header("Content-Disposition: $attachment; filename=\"$fileName\""); |
---|
3206 | |
---|
3207 | |
---|
3208 | if (isset($options['Accept-Ranges']) && $options['Accept-Ranges'] == 1) { |
---|
3209 | |
---|
3210 | header("Accept-Ranges: " . strlen($tmp)); |
---|
3211 | } |
---|
3212 | |
---|
3213 | echo $tmp; |
---|
3214 | |
---|
3215 | flush(); |
---|
3216 | } |
---|
3217 | |
---|
3218 | |
---|
3219 | /** |
---|
3220 | * return the height in units of the current font in the given size |
---|
3221 | */ |
---|
3222 | function getFontHeight($size) { |
---|
3223 | |
---|
3224 | if (!$this->numFonts) { |
---|
3225 | |
---|
3226 | $this->selectFont('./fonts/Helvetica'); |
---|
3227 | } |
---|
3228 | |
---|
3229 | // for the current font, and the given size, what is the height of the font in user units |
---|
3230 | $h = $this->fonts[$this->currentFont]['FontBBox'][3]-$this->fonts[$this->currentFont]['FontBBox'][1]; |
---|
3231 | |
---|
3232 | return $size*$h/1000; |
---|
3233 | } |
---|
3234 | |
---|
3235 | |
---|
3236 | /** |
---|
3237 | * return the font decender, this will normally return a negative number |
---|
3238 | * if you add this number to the baseline, you get the level of the bottom of the font |
---|
3239 | * it is in the pdf user units |
---|
3240 | */ |
---|
3241 | function getFontDecender($size) { |
---|
3242 | |
---|
3243 | // note that this will most likely return a negative value |
---|
3244 | if (!$this->numFonts) { |
---|
3245 | |
---|
3246 | $this->selectFont('./fonts/Helvetica'); |
---|
3247 | } |
---|
3248 | |
---|
3249 | $h = $this->fonts[$this->currentFont]['FontBBox'][1]; |
---|
3250 | |
---|
3251 | return $size*$h/1000; |
---|
3252 | } |
---|
3253 | |
---|
3254 | |
---|
3255 | /** |
---|
3256 | * filter the text, this is applied to all text just before being inserted into the pdf document |
---|
3257 | * it escapes the various things that need to be escaped, and so on |
---|
3258 | * |
---|
3259 | * @access private |
---|
3260 | */ |
---|
3261 | function filterText($text) { |
---|
3262 | |
---|
3263 | $search = array("\\", "(", ")", "<", ">", "'", """, "&"); |
---|
3264 | |
---|
3265 | $replace = array("\\\\", "\(", "\)", "<", ">", "\'", '"', "&"); |
---|
3266 | |
---|
3267 | $text = str_replace($search, $replace, $text); |
---|
3268 | |
---|
3269 | |
---|
3270 | return $text; |
---|
3271 | } |
---|
3272 | |
---|
3273 | |
---|
3274 | /** |
---|
3275 | * given a start position and information about how text is to be laid out, calculate where |
---|
3276 | * on the page the text will end |
---|
3277 | * |
---|
3278 | * @access private |
---|
3279 | */ |
---|
3280 | function PRVTgetTextPosition($x, $y, $angle, $size, $wa, $text) { |
---|
3281 | |
---|
3282 | // given this information return an array containing x and y for the end position as elements 0 and 1 |
---|
3283 | $w = $this->getTextWidth($size, $text); |
---|
3284 | |
---|
3285 | // need to adjust for the number of spaces in this text |
---|
3286 | $words = explode(' ', $text); |
---|
3287 | |
---|
3288 | $nspaces = count($words) -1; |
---|
3289 | |
---|
3290 | $w+= $wa*$nspaces; |
---|
3291 | |
---|
3292 | $a = deg2rad((float)$angle); |
---|
3293 | |
---|
3294 | return array(cos($a) *$w+$x, -sin($a) *$w+$y); |
---|
3295 | } |
---|
3296 | |
---|
3297 | |
---|
3298 | /** |
---|
3299 | * wrapper function for PRVTcheckTextDirective1 |
---|
3300 | * |
---|
3301 | * @access private |
---|
3302 | */ |
---|
3303 | function PRVTcheckTextDirective(&$text, $i, &$f) { |
---|
3304 | |
---|
3305 | return 0; |
---|
3306 | |
---|
3307 | $x = 0; |
---|
3308 | |
---|
3309 | $y = 0; |
---|
3310 | |
---|
3311 | return $this->PRVTcheckTextDirective1($text, $i, $f, 0, $x, $y); |
---|
3312 | } |
---|
3313 | |
---|
3314 | |
---|
3315 | /** |
---|
3316 | * checks if the text stream contains a control directive |
---|
3317 | * if so then makes some changes and returns the number of characters involved in the directive |
---|
3318 | * this has been re-worked to include everything neccesary to fins the current writing point, so that |
---|
3319 | * the location can be sent to the callback function if required |
---|
3320 | * if the directive does not require a font change, then $f should be set to 0 |
---|
3321 | * |
---|
3322 | * @access private |
---|
3323 | */ |
---|
3324 | function PRVTcheckTextDirective1(&$text, $i, &$f, $final, &$x, &$y, $size = 0, $angle = 0, $wordSpaceAdjust = 0) { |
---|
3325 | |
---|
3326 | return 0; |
---|
3327 | |
---|
3328 | $directive = 0; |
---|
3329 | |
---|
3330 | $j = $i; |
---|
3331 | |
---|
3332 | if ($text[$j] == '<') { |
---|
3333 | |
---|
3334 | $j++; |
---|
3335 | |
---|
3336 | switch ($text[$j]) { |
---|
3337 | |
---|
3338 | case '/': |
---|
3339 | |
---|
3340 | $j++; |
---|
3341 | |
---|
3342 | if (strlen($text) <= $j) { |
---|
3343 | |
---|
3344 | return $directive; |
---|
3345 | } |
---|
3346 | |
---|
3347 | switch ($text[$j]) { |
---|
3348 | |
---|
3349 | case 'b': |
---|
3350 | |
---|
3351 | case 'i': |
---|
3352 | |
---|
3353 | $j++; |
---|
3354 | |
---|
3355 | if ($text[$j] == '>') { |
---|
3356 | |
---|
3357 | $p = strrpos($this->currentTextState, $text[$j-1]); |
---|
3358 | |
---|
3359 | if ($p !== false) { |
---|
3360 | |
---|
3361 | // then there is one to remove |
---|
3362 | $this->currentTextState = substr($this->currentTextState, 0, $p) .substr($this->currentTextState, $p+1); |
---|
3363 | } |
---|
3364 | |
---|
3365 | $directive = $j-$i+1; |
---|
3366 | } |
---|
3367 | |
---|
3368 | break; |
---|
3369 | |
---|
3370 | case 'c': |
---|
3371 | |
---|
3372 | // this this might be a callback function |
---|
3373 | $j++; |
---|
3374 | |
---|
3375 | $k = strpos($text, '>', $j); |
---|
3376 | |
---|
3377 | if ($k !== false && $text[$j] == ':') { |
---|
3378 | |
---|
3379 | // then this will be treated as a callback directive |
---|
3380 | $directive = $k-$i+1; |
---|
3381 | |
---|
3382 | $f = 0; |
---|
3383 | |
---|
3384 | // split the remainder on colons to get the function name and the paramater |
---|
3385 | $tmp = substr($text, $j+1, $k-$j-1); |
---|
3386 | |
---|
3387 | $b1 = strpos($tmp, ':'); |
---|
3388 | |
---|
3389 | if ($b1 !== false) { |
---|
3390 | |
---|
3391 | $func = substr($tmp, 0, $b1); |
---|
3392 | |
---|
3393 | $parm = substr($tmp, $b1+1); |
---|
3394 | } else { |
---|
3395 | |
---|
3396 | $func = $tmp; |
---|
3397 | |
---|
3398 | $parm = ''; |
---|
3399 | } |
---|
3400 | |
---|
3401 | if (!isset($func) || !strlen(trim($func))) { |
---|
3402 | |
---|
3403 | $directive = 0; |
---|
3404 | } else { |
---|
3405 | |
---|
3406 | // only call the function if this is the final call |
---|
3407 | if ($final) { |
---|
3408 | |
---|
3409 | // need to assess the text position, calculate the text width to this point |
---|
3410 | // can use getTextWidth to find the text width I think |
---|
3411 | $tmp = $this->PRVTgetTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, substr($text, 0, $i)); |
---|
3412 | |
---|
3413 | $info = array('x' => $tmp[0], 'y' => $tmp[1], 'angle' => $angle, 'status' => 'end', 'p' => $parm, 'nCallback' => $this->nCallback); |
---|
3414 | |
---|
3415 | $x = $tmp[0]; |
---|
3416 | |
---|
3417 | $y = $tmp[1]; |
---|
3418 | |
---|
3419 | $ret = $this->$func($info); |
---|
3420 | |
---|
3421 | if (is_array($ret)) { |
---|
3422 | |
---|
3423 | // then the return from the callback function could set the position, to start with, later will do font colour, and font |
---|
3424 | foreach($ret as $rk => $rv) { |
---|
3425 | |
---|
3426 | switch ($rk) { |
---|
3427 | |
---|
3428 | case 'x': |
---|
3429 | |
---|
3430 | case 'y': |
---|
3431 | |
---|
3432 | $$rk = $rv; |
---|
3433 | |
---|
3434 | break; |
---|
3435 | } |
---|
3436 | } |
---|
3437 | } |
---|
3438 | |
---|
3439 | // also remove from to the stack |
---|
3440 | // for simplicity, just take from the end, fix this another day |
---|
3441 | $this->nCallback--; |
---|
3442 | |
---|
3443 | if ($this->nCallback<0) { |
---|
3444 | |
---|
3445 | $this->nCallBack = 0; |
---|
3446 | } |
---|
3447 | } |
---|
3448 | } |
---|
3449 | } |
---|
3450 | |
---|
3451 | break; |
---|
3452 | } |
---|
3453 | |
---|
3454 | break; |
---|
3455 | |
---|
3456 | case 'b': |
---|
3457 | |
---|
3458 | case 'i': |
---|
3459 | |
---|
3460 | $j++; |
---|
3461 | |
---|
3462 | if ($text[$j] == '>') { |
---|
3463 | |
---|
3464 | $this->currentTextState.= $text[$j-1]; |
---|
3465 | |
---|
3466 | $directive = $j-$i+1; |
---|
3467 | } |
---|
3468 | |
---|
3469 | break; |
---|
3470 | |
---|
3471 | case 'C': |
---|
3472 | |
---|
3473 | $noClose = 1; |
---|
3474 | |
---|
3475 | case 'c': |
---|
3476 | |
---|
3477 | // this this might be a callback function |
---|
3478 | $j++; |
---|
3479 | |
---|
3480 | $k = strpos($text, '>', $j); |
---|
3481 | |
---|
3482 | if ($k !== false && $text[$j] == ':') { |
---|
3483 | |
---|
3484 | // then this will be treated as a callback directive |
---|
3485 | $directive = $k-$i+1; |
---|
3486 | |
---|
3487 | $f = 0; |
---|
3488 | |
---|
3489 | // split the remainder on colons to get the function name and the paramater |
---|
3490 | // $bits = explode(':',substr($text,$j+1,$k-$j-1)); |
---|
3491 | $tmp = substr($text, $j+1, $k-$j-1); |
---|
3492 | |
---|
3493 | $b1 = strpos($tmp, ':'); |
---|
3494 | |
---|
3495 | if ($b1 !== false) { |
---|
3496 | |
---|
3497 | $func = substr($tmp, 0, $b1); |
---|
3498 | |
---|
3499 | $parm = substr($tmp, $b1+1); |
---|
3500 | } else { |
---|
3501 | |
---|
3502 | $func = $tmp; |
---|
3503 | |
---|
3504 | $parm = ''; |
---|
3505 | } |
---|
3506 | |
---|
3507 | if (!isset($func) || !strlen(trim($func))) { |
---|
3508 | |
---|
3509 | $directive = 0; |
---|
3510 | } else { |
---|
3511 | |
---|
3512 | // only call the function if this is the final call, ie, the one actually doing printing, not measurement |
---|
3513 | if ($final) { |
---|
3514 | |
---|
3515 | // need to assess the text position, calculate the text width to this point |
---|
3516 | // can use getTextWidth to find the text width I think |
---|
3517 | // also add the text height and decender |
---|
3518 | $tmp = $this->PRVTgetTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, substr($text, 0, $i)); |
---|
3519 | |
---|
3520 | $info = array('x' => $tmp[0], 'y' => $tmp[1], 'angle' => $angle, 'status' => 'start', 'p' => $parm, 'f' => $func, 'height' => $this->getFontHeight($size), 'decender' => $this->getFontDecender($size)); |
---|
3521 | |
---|
3522 | $x = $tmp[0]; |
---|
3523 | |
---|
3524 | $y = $tmp[1]; |
---|
3525 | |
---|
3526 | if (!isset($noClose) || !$noClose) { |
---|
3527 | |
---|
3528 | // only add to the stack if this is a small 'c', therefore is a start-stop pair |
---|
3529 | $this->nCallback++; |
---|
3530 | |
---|
3531 | $info['nCallback'] = $this->nCallback; |
---|
3532 | |
---|
3533 | $this->callback[$this->nCallback] = $info; |
---|
3534 | } |
---|
3535 | |
---|
3536 | $ret = $this->$func($info); |
---|
3537 | |
---|
3538 | if (is_array($ret)) { |
---|
3539 | |
---|
3540 | // then the return from the callback function could set the position, to start with, later will do font colour, and font |
---|
3541 | foreach($ret as $rk => $rv) { |
---|
3542 | |
---|
3543 | switch ($rk) { |
---|
3544 | |
---|
3545 | case 'x': |
---|
3546 | |
---|
3547 | case 'y': |
---|
3548 | |
---|
3549 | $$rk = $rv; |
---|
3550 | |
---|
3551 | break; |
---|
3552 | } |
---|
3553 | } |
---|
3554 | } |
---|
3555 | } |
---|
3556 | } |
---|
3557 | } |
---|
3558 | |
---|
3559 | break; |
---|
3560 | } |
---|
3561 | } |
---|
3562 | |
---|
3563 | return $directive; |
---|
3564 | } |
---|
3565 | |
---|
3566 | |
---|
3567 | /** |
---|
3568 | * add text to the document, at a specified location, size and angle on the page |
---|
3569 | */ |
---|
3570 | function addText($x, $y, $size, $text, $angle = 0, $wordSpaceAdjust = 0) { |
---|
3571 | |
---|
3572 | if (!$this->numFonts) { |
---|
3573 | $this->selectFont('./fonts/Helvetica'); |
---|
3574 | } |
---|
3575 | |
---|
3576 | |
---|
3577 | // if there are any open callbacks, then they should be called, to show the start of the line |
---|
3578 | if ($this->nCallback>0) { |
---|
3579 | |
---|
3580 | for ($i = $this->nCallback;$i>0;$i--) { |
---|
3581 | |
---|
3582 | // call each function |
---|
3583 | $info = array('x' => $x, |
---|
3584 | 'y' => $y, |
---|
3585 | 'angle' => $angle, |
---|
3586 | 'status' => 'sol', |
---|
3587 | 'p' => $this->callback[$i]['p'], |
---|
3588 | 'nCallback' => $this->callback[$i]['nCallback'], |
---|
3589 | 'height' => $this->callback[$i]['height'], |
---|
3590 | 'decender' => $this->callback[$i]['decender']); |
---|
3591 | |
---|
3592 | $func = $this->callback[$i]['f']; |
---|
3593 | |
---|
3594 | $this->$func($info); |
---|
3595 | } |
---|
3596 | } |
---|
3597 | |
---|
3598 | if ($angle == 0) { |
---|
3599 | |
---|
3600 | $this->objects[$this->currentContents]['c'].= "\n".'BT '.sprintf('%.3f', $x) .' '.sprintf('%.3f', $y) .' Td'; |
---|
3601 | |
---|
3602 | } else { |
---|
3603 | |
---|
3604 | $a = deg2rad((float)$angle); |
---|
3605 | |
---|
3606 | $tmp = "\n".'BT '; |
---|
3607 | |
---|
3608 | $tmp.= sprintf('%.3f', cos($a)) .' '.sprintf('%.3f', (-1.0*sin($a))) .' '.sprintf('%.3f', sin($a)) .' '.sprintf('%.3f', cos($a)) .' '; |
---|
3609 | |
---|
3610 | $tmp.= sprintf('%.3f', $x) .' '.sprintf('%.3f', $y) .' Tm'; |
---|
3611 | |
---|
3612 | $this->objects[$this->currentContents]['c'].= $tmp; |
---|
3613 | } |
---|
3614 | |
---|
3615 | if ($wordSpaceAdjust != 0 || $wordSpaceAdjust != $this->wordSpaceAdjust) { |
---|
3616 | |
---|
3617 | $this->wordSpaceAdjust = $wordSpaceAdjust; |
---|
3618 | |
---|
3619 | $this->objects[$this->currentContents]['c'].= ' '.sprintf('%.3f', $wordSpaceAdjust) .' Tw'; |
---|
3620 | } |
---|
3621 | |
---|
3622 | $len = strlen($text); |
---|
3623 | |
---|
3624 | $start = 0; |
---|
3625 | |
---|
3626 | /* |
---|
3627 | for ($i = 0;$i<$len;$i++){ |
---|
3628 | $f = 1; |
---|
3629 | $directive = 0; //$this->PRVTcheckTextDirective($text,$i,$f); |
---|
3630 | if ($directive){ |
---|
3631 | // then we should write what we need to |
---|
3632 | if ($i>$start){ |
---|
3633 | $part = substr($text,$start,$i-$start); |
---|
3634 | $this->objects[$this->currentContents]['c'] .= ' /F'.$this->currentFontNum.' '.sprintf('%.1f',$size).' Tf '; |
---|
3635 | $this->objects[$this->currentContents]['c'] .= ' ('.$this->filterText($part).') Tj'; |
---|
3636 | } |
---|
3637 | if ($f){ |
---|
3638 | // then there was nothing drastic done here, restore the contents |
---|
3639 | $this->setCurrentFont(); |
---|
3640 | } else { |
---|
3641 | $this->objects[$this->currentContents]['c'] .= ' ET'; |
---|
3642 | $f = 1; |
---|
3643 | $xp = $x; |
---|
3644 | $yp = $y; |
---|
3645 | $directive = 0; //$this->PRVTcheckTextDirective1($text,$i,$f,1,$xp,$yp,$size,$angle,$wordSpaceAdjust); |
---|
3646 | |
---|
3647 | // restart the text object |
---|
3648 | if ($angle == 0){ |
---|
3649 | $this->objects[$this->currentContents]['c'] .= "\n".'BT '.sprintf('%.3f',$xp).' '.sprintf('%.3f',$yp).' Td'; |
---|
3650 | } else { |
---|
3651 | $a = deg2rad((float)$angle); |
---|
3652 | $tmp = "\n".'BT '; |
---|
3653 | $tmp .= sprintf('%.3f',cos($a)).' '.sprintf('%.3f',(-1.0*sin($a))).' '.sprintf('%.3f',sin($a)).' '.sprintf('%.3f',cos($a)).' '; |
---|
3654 | $tmp .= sprintf('%.3f',$xp).' '.sprintf('%.3f',$yp).' Tm'; |
---|
3655 | $this->objects[$this->currentContents]['c'] .= $tmp; |
---|
3656 | } |
---|
3657 | if ($wordSpaceAdjust != 0 || $wordSpaceAdjust != $this->wordSpaceAdjust){ |
---|
3658 | $this->wordSpaceAdjust = $wordSpaceAdjust; |
---|
3659 | $this->objects[$this->currentContents]['c'] .= ' '.sprintf('%.3f',$wordSpaceAdjust).' Tw'; |
---|
3660 | } |
---|
3661 | } |
---|
3662 | // and move the writing point to the next piece of text |
---|
3663 | $i = $i+$directive-1; |
---|
3664 | $start = $i+1; |
---|
3665 | } |
---|
3666 | |
---|
3667 | } |
---|
3668 | */ |
---|
3669 | if ($start < $len) { |
---|
3670 | |
---|
3671 | $part = substr($text, $start); |
---|
3672 | |
---|
3673 | $this->objects[$this->currentContents]['c'].= ' /F'.$this->currentFontNum.' '.sprintf('%.1f', $size) .' Tf '; |
---|
3674 | |
---|
3675 | $this->objects[$this->currentContents]['c'].= ' ('.$this->filterText($part) .') Tj'; |
---|
3676 | } |
---|
3677 | |
---|
3678 | $this->objects[$this->currentContents]['c'].= ' ET'; |
---|
3679 | |
---|
3680 | |
---|
3681 | // if there are any open callbacks, then they should be called, to show the end of the line |
---|
3682 | if ($this->nCallback>0) { |
---|
3683 | |
---|
3684 | for ($i = $this->nCallback;$i>0;$i--) { |
---|
3685 | |
---|
3686 | // call each function |
---|
3687 | $tmp = $this->PRVTgetTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, $text); |
---|
3688 | |
---|
3689 | $info = array('x' => $tmp[0], 'y' => $tmp[1], 'angle' => $angle, 'status' => 'eol', 'p' => $this->callback[$i]['p'], 'nCallback' => $this->callback[$i]['nCallback'], 'height' => $this->callback[$i]['height'], 'decender' => $this->callback[$i]['decender']); |
---|
3690 | |
---|
3691 | $func = $this->callback[$i]['f']; |
---|
3692 | |
---|
3693 | $this->$func($info); |
---|
3694 | } |
---|
3695 | } |
---|
3696 | } |
---|
3697 | |
---|
3698 | |
---|
3699 | /** |
---|
3700 | * calculate how wide a given text string will be on a page, at a given size. |
---|
3701 | * this can be called externally, but is alse used by the other class functions |
---|
3702 | */ |
---|
3703 | function getTextWidth($size, $text, $spacing = 0) { |
---|
3704 | |
---|
3705 | // this function should not change any of the settings, though it will need to |
---|
3706 | // track any directives which change during calculation, so copy them at the start |
---|
3707 | // and put them back at the end. |
---|
3708 | $store_currentTextState = $this->currentTextState; |
---|
3709 | |
---|
3710 | |
---|
3711 | if (!$this->numFonts) { |
---|
3712 | |
---|
3713 | $this->selectFont('./fonts/Helvetica'); |
---|
3714 | } |
---|
3715 | |
---|
3716 | |
---|
3717 | // converts a number or a float to a string so it can get the width |
---|
3718 | $text = "$text"; |
---|
3719 | |
---|
3720 | |
---|
3721 | // hmm, this is where it all starts to get tricky - use the font information to |
---|
3722 | // calculate the width of each character, add them up and convert to user units |
---|
3723 | $w = 0; |
---|
3724 | |
---|
3725 | $len = strlen($text); |
---|
3726 | |
---|
3727 | $cf = $this->currentFont; |
---|
3728 | |
---|
3729 | $space_scale = 1000 / $size; |
---|
3730 | |
---|
3731 | for ($i = 0; $i < $len; $i++) { |
---|
3732 | |
---|
3733 | // $f = 1; |
---|
3734 | // $directive = 0; //$this->PRVTcheckTextDirective($text,$i,$f); |
---|
3735 | // if ($directive){ |
---|
3736 | // if ($f){ |
---|
3737 | // $this->setCurrentFont(); |
---|
3738 | // $cf = $this->currentFont; |
---|
3739 | // } |
---|
3740 | // $i = $i+$directive-1; |
---|
3741 | // } else { |
---|
3742 | $char = ord($text{$i}); |
---|
3743 | |
---|
3744 | if ( isset($this->fonts[$cf]['differences'][$char])) { |
---|
3745 | |
---|
3746 | |
---|
3747 | // then this character is being replaced by another |
---|
3748 | $name = $this->fonts[$cf]['differences'][$char]; |
---|
3749 | |
---|
3750 | |
---|
3751 | if ( isset($this->fonts[$cf]['C'][$name]['WX'])) |
---|
3752 | $w+= $this->fonts[$cf]['C'][$name]['WX']; |
---|
3753 | } else if (isset($this->fonts[$cf]['C'][$char]['WX'])) |
---|
3754 | $w+= $this->fonts[$cf]['C'][$char]['WX']; |
---|
3755 | |
---|
3756 | |
---|
3757 | if ( $char == 32) // Space |
---|
3758 | $w+= $spacing * $space_scale; |
---|
3759 | } |
---|
3760 | |
---|
3761 | |
---|
3762 | |
---|
3763 | $this->currentTextState = $store_currentTextState; |
---|
3764 | |
---|
3765 | $this->setCurrentFont(); |
---|
3766 | |
---|
3767 | |
---|
3768 | return $w*$size/1000; |
---|
3769 | } |
---|
3770 | |
---|
3771 | |
---|
3772 | /** |
---|
3773 | * do a part of the calculation for sorting out the justification of the text |
---|
3774 | * |
---|
3775 | * @access private |
---|
3776 | */ |
---|
3777 | function PRVTadjustWrapText($text, $actual, $width, &$x, &$adjust, $justification) { |
---|
3778 | |
---|
3779 | switch ($justification) { |
---|
3780 | |
---|
3781 | case 'left': |
---|
3782 | |
---|
3783 | return; |
---|
3784 | |
---|
3785 | break; |
---|
3786 | |
---|
3787 | case 'right': |
---|
3788 | |
---|
3789 | $x+= $width-$actual; |
---|
3790 | |
---|
3791 | break; |
---|
3792 | |
---|
3793 | case 'center': |
---|
3794 | |
---|
3795 | case 'centre': |
---|
3796 | |
---|
3797 | $x+= ($width-$actual) /2; |
---|
3798 | |
---|
3799 | break; |
---|
3800 | |
---|
3801 | case 'full': |
---|
3802 | |
---|
3803 | // count the number of words |
---|
3804 | $words = explode(' ', $text); |
---|
3805 | |
---|
3806 | $nspaces = count($words) -1; |
---|
3807 | |
---|
3808 | if ($nspaces>0) { |
---|
3809 | |
---|
3810 | $adjust = ($width-$actual) /$nspaces; |
---|
3811 | } else { |
---|
3812 | |
---|
3813 | $adjust = 0; |
---|
3814 | } |
---|
3815 | |
---|
3816 | break; |
---|
3817 | } |
---|
3818 | } |
---|
3819 | |
---|
3820 | |
---|
3821 | /** |
---|
3822 | * add text to the page, but ensure that it fits within a certain width |
---|
3823 | * if it does not fit then put in as much as possible, splitting at word boundaries |
---|
3824 | * and return the remainder. |
---|
3825 | * justification and angle can also be specified for the text |
---|
3826 | */ |
---|
3827 | function addTextWrap($x, $y, $width, $size, $text, $justification = 'left', $angle = 0, $test = 0) { |
---|
3828 | |
---|
3829 | // this will display the text, and if it goes beyond the width $width, will backtrack to the |
---|
3830 | // previous space or hyphen, and return the remainder of the text. |
---|
3831 | |
---|
3832 | // $justification can be set to 'left','right','center','centre','full' |
---|
3833 | |
---|
3834 | // need to store the initial text state, as this will change during the width calculation |
---|
3835 | // but will need to be re-set before printing, so that the chars work out right |
---|
3836 | $store_currentTextState = $this->currentTextState; |
---|
3837 | |
---|
3838 | |
---|
3839 | if (!$this->numFonts) { |
---|
3840 | $this->selectFont('./fonts/Helvetica'); |
---|
3841 | } |
---|
3842 | |
---|
3843 | if ($width <= 0) { |
---|
3844 | |
---|
3845 | // error, pretend it printed ok, otherwise risking a loop |
---|
3846 | return ''; |
---|
3847 | } |
---|
3848 | |
---|
3849 | $w = 0; |
---|
3850 | |
---|
3851 | $break = 0; |
---|
3852 | |
---|
3853 | $breakWidth = 0; |
---|
3854 | |
---|
3855 | $len = strlen($text); |
---|
3856 | |
---|
3857 | $cf = $this->currentFont; |
---|
3858 | |
---|
3859 | $tw = $width/$size*1000; |
---|
3860 | |
---|
3861 | for ($i = 0;$i<$len;$i++) { |
---|
3862 | |
---|
3863 | $f = 1; |
---|
3864 | |
---|
3865 | $directive = 0; |
---|
3866 | //$this->PRVTcheckTextDirective($text,$i,$f); |
---|
3867 | if ($directive) { |
---|
3868 | |
---|
3869 | if ($f) { |
---|
3870 | |
---|
3871 | $this->setCurrentFont(); |
---|
3872 | |
---|
3873 | $cf = $this->currentFont; |
---|
3874 | } |
---|
3875 | |
---|
3876 | $i = $i+$directive-1; |
---|
3877 | } else { |
---|
3878 | |
---|
3879 | $cOrd = ord($text[$i]); |
---|
3880 | |
---|
3881 | if (isset($this->fonts[$cf]['differences'][$cOrd])) { |
---|
3882 | |
---|
3883 | // then this character is being replaced by another |
---|
3884 | $cOrd2 = $this->fonts[$cf]['differences'][$cOrd]; |
---|
3885 | } else { |
---|
3886 | |
---|
3887 | $cOrd2 = $cOrd; |
---|
3888 | } |
---|
3889 | |
---|
3890 | |
---|
3891 | if (isset($this->fonts[$cf]['C'][$cOrd2]['WX'])) { |
---|
3892 | |
---|
3893 | $w+= $this->fonts[$cf]['C'][$cOrd2]['WX']; |
---|
3894 | } |
---|
3895 | |
---|
3896 | if ($w>$tw) { |
---|
3897 | |
---|
3898 | // then we need to truncate this line |
---|
3899 | if ($break>0) { |
---|
3900 | |
---|
3901 | // then we have somewhere that we can split :) |
---|
3902 | if ($text[$break] == ' ') { |
---|
3903 | |
---|
3904 | $tmp = substr($text, 0, $break); |
---|
3905 | } else { |
---|
3906 | |
---|
3907 | $tmp = substr($text, 0, $break+1); |
---|
3908 | } |
---|
3909 | |
---|
3910 | $adjust = 0; |
---|
3911 | |
---|
3912 | $this->PRVTadjustWrapText($tmp, $breakWidth, $width, $x, $adjust, $justification); |
---|
3913 | |
---|
3914 | |
---|
3915 | // reset the text state |
---|
3916 | $this->currentTextState = $store_currentTextState; |
---|
3917 | |
---|
3918 | $this->setCurrentFont(); |
---|
3919 | |
---|
3920 | if (!$test) { |
---|
3921 | |
---|
3922 | $this->addText($x, $y, $size, $tmp, $angle, $adjust); |
---|
3923 | } |
---|
3924 | |
---|
3925 | return substr($text, $break+1); |
---|
3926 | } else { |
---|
3927 | |
---|
3928 | // just split before the current character |
---|
3929 | $tmp = substr($text, 0, $i); |
---|
3930 | |
---|
3931 | $adjust = 0; |
---|
3932 | |
---|
3933 | $ctmp = ord($text[$i]); |
---|
3934 | |
---|
3935 | if (isset($this->fonts[$cf]['differences'][$ctmp])) { |
---|
3936 | |
---|
3937 | $ctmp = $this->fonts[$cf]['differences'][$ctmp]; |
---|
3938 | } |
---|
3939 | |
---|
3940 | $tmpw = ($w-$this->fonts[$cf]['C'][$ctmp]['WX']) *$size/1000; |
---|
3941 | |
---|
3942 | $this->PRVTadjustWrapText($tmp, $tmpw, $width, $x, $adjust, $justification); |
---|
3943 | |
---|
3944 | // reset the text state |
---|
3945 | $this->currentTextState = $store_currentTextState; |
---|
3946 | |
---|
3947 | $this->setCurrentFont(); |
---|
3948 | |
---|
3949 | if (!$test) { |
---|
3950 | |
---|
3951 | $this->addText($x, $y, $size, $tmp, $angle, $adjust); |
---|
3952 | } |
---|
3953 | |
---|
3954 | return substr($text, $i); |
---|
3955 | } |
---|
3956 | } |
---|
3957 | |
---|
3958 | if ($text[$i] == '-') { |
---|
3959 | |
---|
3960 | $break = $i; |
---|
3961 | |
---|
3962 | $breakWidth = $w*$size/1000; |
---|
3963 | } |
---|
3964 | |
---|
3965 | if ($text[$i] == ' ') { |
---|
3966 | |
---|
3967 | $break = $i; |
---|
3968 | |
---|
3969 | $ctmp = ord($text[$i]); |
---|
3970 | |
---|
3971 | if (isset($this->fonts[$cf]['differences'][$ctmp])) { |
---|
3972 | |
---|
3973 | $ctmp = $this->fonts[$cf]['differences'][$ctmp]; |
---|
3974 | } |
---|
3975 | |
---|
3976 | $breakWidth = ($w-$this->fonts[$cf]['C'][$ctmp]['WX']) *$size/1000; |
---|
3977 | } |
---|
3978 | } |
---|
3979 | } |
---|
3980 | |
---|
3981 | // then there was no need to break this line |
---|
3982 | if ($justification == 'full') { |
---|
3983 | |
---|
3984 | $justification = 'left'; |
---|
3985 | } |
---|
3986 | |
---|
3987 | $adjust = 0; |
---|
3988 | |
---|
3989 | $tmpw = $w*$size/1000; |
---|
3990 | |
---|
3991 | $this->PRVTadjustWrapText($text, $tmpw, $width, $x, $adjust, $justification); |
---|
3992 | |
---|
3993 | // reset the text state |
---|
3994 | $this->currentTextState = $store_currentTextState; |
---|
3995 | |
---|
3996 | $this->setCurrentFont(); |
---|
3997 | |
---|
3998 | if (!$test) { |
---|
3999 | |
---|
4000 | $this->addText($x, $y, $size, $text, $angle, $adjust, $angle); |
---|
4001 | } |
---|
4002 | |
---|
4003 | return ''; |
---|
4004 | } |
---|
4005 | |
---|
4006 | |
---|
4007 | /** |
---|
4008 | * this will be called at a new page to return the state to what it was on the |
---|
4009 | * end of the previous page, before the stack was closed down |
---|
4010 | * This is to get around not being able to have open 'q' across pages |
---|
4011 | * |
---|
4012 | */ |
---|
4013 | function saveState($pageEnd = 0) { |
---|
4014 | |
---|
4015 | if ($pageEnd) { |
---|
4016 | |
---|
4017 | // this will be called at a new page to return the state to what it was on the |
---|
4018 | // end of the previous page, before the stack was closed down |
---|
4019 | // This is to get around not being able to have open 'q' across pages |
---|
4020 | $opt = $this->stateStack[$pageEnd]; |
---|
4021 | // ok to use this as stack starts numbering at 1 |
---|
4022 | $this->setColor($opt['col']['r'], $opt['col']['g'], $opt['col']['b'], 1); |
---|
4023 | |
---|
4024 | $this->setStrokeColor($opt['str']['r'], $opt['str']['g'], $opt['str']['b'], 1); |
---|
4025 | |
---|
4026 | $this->objects[$this->currentContents]['c'].= "\n".$opt['lin']; |
---|
4027 | |
---|
4028 | // $this->currentLineStyle = $opt['lin']; |
---|
4029 | |
---|
4030 | } else { |
---|
4031 | |
---|
4032 | $this->nStateStack++; |
---|
4033 | |
---|
4034 | $this->stateStack[$this->nStateStack] = array( |
---|
4035 | 'col' => $this->currentColour, 'str' => $this->currentStrokeColour, 'lin' => $this->currentLineStyle); |
---|
4036 | } |
---|
4037 | |
---|
4038 | $this->objects[$this->currentContents]['c'].= "\nq"; |
---|
4039 | } |
---|
4040 | |
---|
4041 | |
---|
4042 | /** |
---|
4043 | * restore a previously saved state |
---|
4044 | */ |
---|
4045 | function restoreState($pageEnd = 0) { |
---|
4046 | |
---|
4047 | if (!$pageEnd) { |
---|
4048 | |
---|
4049 | $n = $this->nStateStack; |
---|
4050 | |
---|
4051 | $this->currentColour = $this->stateStack[$n]['col']; |
---|
4052 | |
---|
4053 | $this->currentStrokeColour = $this->stateStack[$n]['str']; |
---|
4054 | |
---|
4055 | $this->objects[$this->currentContents]['c'].= "\n".$this->stateStack[$n]['lin']; |
---|
4056 | |
---|
4057 | $this->currentLineStyle = $this->stateStack[$n]['lin']; |
---|
4058 | |
---|
4059 | unset($this->stateStack[$n]); |
---|
4060 | |
---|
4061 | $this->nStateStack--; |
---|
4062 | } |
---|
4063 | |
---|
4064 | $this->objects[$this->currentContents]['c'].= "\nQ"; |
---|
4065 | } |
---|
4066 | |
---|
4067 | |
---|
4068 | /** |
---|
4069 | * make a loose object, the output will go into this object, until it is closed, then will revert to |
---|
4070 | * the current one. |
---|
4071 | * this object will not appear until it is included within a page. |
---|
4072 | * the function will return the object number |
---|
4073 | */ |
---|
4074 | function openObject() { |
---|
4075 | |
---|
4076 | $this->nStack++; |
---|
4077 | |
---|
4078 | $this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage); |
---|
4079 | |
---|
4080 | // add a new object of the content type, to hold the data flow |
---|
4081 | $this->numObj++; |
---|
4082 | |
---|
4083 | $this->o_contents($this->numObj, 'new'); |
---|
4084 | |
---|
4085 | $this->currentContents = $this->numObj; |
---|
4086 | |
---|
4087 | $this->looseObjects[$this->numObj] = 1; |
---|
4088 | |
---|
4089 | |
---|
4090 | return $this->numObj; |
---|
4091 | } |
---|
4092 | |
---|
4093 | |
---|
4094 | /** |
---|
4095 | * open an existing object for editing |
---|
4096 | */ |
---|
4097 | function reopenObject($id) { |
---|
4098 | |
---|
4099 | $this->nStack++; |
---|
4100 | |
---|
4101 | $this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage); |
---|
4102 | |
---|
4103 | $this->currentContents = $id; |
---|
4104 | |
---|
4105 | // also if this object is the primary contents for a page, then set the current page to its parent |
---|
4106 | if (isset($this->objects[$id]['onPage'])) { |
---|
4107 | |
---|
4108 | $this->currentPage = $this->objects[$id]['onPage']; |
---|
4109 | } |
---|
4110 | } |
---|
4111 | |
---|
4112 | |
---|
4113 | /** |
---|
4114 | * close an object |
---|
4115 | */ |
---|
4116 | function closeObject() { |
---|
4117 | |
---|
4118 | // close the object, as long as there was one open in the first place, which will be indicated by |
---|
4119 | // an objectId on the stack. |
---|
4120 | if ($this->nStack>0) { |
---|
4121 | |
---|
4122 | $this->currentContents = $this->stack[$this->nStack]['c']; |
---|
4123 | |
---|
4124 | $this->currentPage = $this->stack[$this->nStack]['p']; |
---|
4125 | |
---|
4126 | $this->nStack--; |
---|
4127 | |
---|
4128 | // easier to probably not worry about removing the old entries, they will be overwritten |
---|
4129 | // if there are new ones. |
---|
4130 | |
---|
4131 | } |
---|
4132 | } |
---|
4133 | |
---|
4134 | |
---|
4135 | /** |
---|
4136 | * stop an object from appearing on pages from this point on |
---|
4137 | */ |
---|
4138 | function stopObject($id) { |
---|
4139 | |
---|
4140 | // if an object has been appearing on pages up to now, then stop it, this page will |
---|
4141 | // be the last one that could contian it. |
---|
4142 | if (isset($this->addLooseObjects[$id])) { |
---|
4143 | |
---|
4144 | $this->addLooseObjects[$id] = ''; |
---|
4145 | } |
---|
4146 | } |
---|
4147 | |
---|
4148 | |
---|
4149 | /** |
---|
4150 | * after an object has been created, it wil only show if it has been added, using this function. |
---|
4151 | */ |
---|
4152 | function addObject($id, $options = 'add') { |
---|
4153 | |
---|
4154 | // add the specified object to the page |
---|
4155 | if (isset($this->looseObjects[$id]) && $this->currentContents != $id) { |
---|
4156 | |
---|
4157 | // then it is a valid object, and it is not being added to itself |
---|
4158 | switch ($options) { |
---|
4159 | |
---|
4160 | case 'all': |
---|
4161 | |
---|
4162 | // then this object is to be added to this page (done in the next block) and |
---|
4163 | // all future new pages. |
---|
4164 | $this->addLooseObjects[$id] = 'all'; |
---|
4165 | |
---|
4166 | case 'add': |
---|
4167 | |
---|
4168 | if (isset($this->objects[$this->currentContents]['onPage'])) { |
---|
4169 | |
---|
4170 | // then the destination contents is the primary for the page |
---|
4171 | // (though this object is actually added to that page) |
---|
4172 | $this->o_page($this->objects[$this->currentContents]['onPage'], 'content', $id); |
---|
4173 | } |
---|
4174 | |
---|
4175 | break; |
---|
4176 | |
---|
4177 | case 'even': |
---|
4178 | |
---|
4179 | $this->addLooseObjects[$id] = 'even'; |
---|
4180 | |
---|
4181 | $pageObjectId = $this->objects[$this->currentContents]['onPage']; |
---|
4182 | |
---|
4183 | if ($this->objects[$pageObjectId]['info']['pageNum']%2 == 0) { |
---|
4184 | |
---|
4185 | $this->addObject($id); |
---|
4186 | // hacky huh :) |
---|
4187 | |
---|
4188 | } |
---|
4189 | |
---|
4190 | break; |
---|
4191 | |
---|
4192 | case 'odd': |
---|
4193 | |
---|
4194 | $this->addLooseObjects[$id] = 'odd'; |
---|
4195 | |
---|
4196 | $pageObjectId = $this->objects[$this->currentContents]['onPage']; |
---|
4197 | |
---|
4198 | if ($this->objects[$pageObjectId]['info']['pageNum']%2 == 1) { |
---|
4199 | |
---|
4200 | $this->addObject($id); |
---|
4201 | // hacky huh :) |
---|
4202 | |
---|
4203 | } |
---|
4204 | |
---|
4205 | break; |
---|
4206 | |
---|
4207 | case 'next': |
---|
4208 | |
---|
4209 | $this->addLooseObjects[$id] = 'all'; |
---|
4210 | |
---|
4211 | break; |
---|
4212 | |
---|
4213 | case 'nexteven': |
---|
4214 | |
---|
4215 | $this->addLooseObjects[$id] = 'even'; |
---|
4216 | |
---|
4217 | break; |
---|
4218 | |
---|
4219 | case 'nextodd': |
---|
4220 | |
---|
4221 | $this->addLooseObjects[$id] = 'odd'; |
---|
4222 | |
---|
4223 | break; |
---|
4224 | } |
---|
4225 | } |
---|
4226 | } |
---|
4227 | |
---|
4228 | |
---|
4229 | /** |
---|
4230 | * return a storable representation of a specific object |
---|
4231 | */ |
---|
4232 | function serializeObject($id) { |
---|
4233 | |
---|
4234 | if ( array_key_exists($id, $this->objects)) |
---|
4235 | return var_export($this->objects[$id], true); |
---|
4236 | |
---|
4237 | |
---|
4238 | return null; |
---|
4239 | } |
---|
4240 | |
---|
4241 | |
---|
4242 | /** |
---|
4243 | * restore an object from its stored representation. returns its new object id. |
---|
4244 | */ |
---|
4245 | function restoreSerializedObject($obj) { |
---|
4246 | |
---|
4247 | |
---|
4248 | $obj_id = $this->openObject(); |
---|
4249 | |
---|
4250 | eval('$this->objects[$obj_id] = ' . $obj . ';'); |
---|
4251 | |
---|
4252 | $this->closeObject(); |
---|
4253 | |
---|
4254 | return $obj_id; |
---|
4255 | } |
---|
4256 | |
---|
4257 | |
---|
4258 | |
---|
4259 | /** |
---|
4260 | * add content to the documents info object |
---|
4261 | */ |
---|
4262 | function addInfo($label, $value = 0) { |
---|
4263 | |
---|
4264 | // this will only work if the label is one of the valid ones. |
---|
4265 | // modify this so that arrays can be passed as well. |
---|
4266 | // if $label is an array then assume that it is key => value pairs |
---|
4267 | // else assume that they are both scalar, anything else will probably error |
---|
4268 | if (is_array($label)) { |
---|
4269 | |
---|
4270 | foreach ($label as $l => $v) { |
---|
4271 | |
---|
4272 | $this->o_info($this->infoObject, $l, $v); |
---|
4273 | } |
---|
4274 | } else { |
---|
4275 | |
---|
4276 | $this->o_info($this->infoObject, $label, $value); |
---|
4277 | } |
---|
4278 | } |
---|
4279 | |
---|
4280 | |
---|
4281 | /** |
---|
4282 | * set the viewer preferences of the document, it is up to the browser to obey these. |
---|
4283 | */ |
---|
4284 | function setPreferences($label, $value = 0) { |
---|
4285 | |
---|
4286 | // this will only work if the label is one of the valid ones. |
---|
4287 | if (is_array($label)) { |
---|
4288 | |
---|
4289 | foreach ($label as $l => $v) { |
---|
4290 | |
---|
4291 | $this->o_catalog($this->catalogId, 'viewerPreferences', array($l => $v)); |
---|
4292 | } |
---|
4293 | } else { |
---|
4294 | |
---|
4295 | $this->o_catalog($this->catalogId, 'viewerPreferences', array($label => $value)); |
---|
4296 | } |
---|
4297 | } |
---|
4298 | |
---|
4299 | |
---|
4300 | /** |
---|
4301 | * extract an integer from a position in a byte stream |
---|
4302 | * |
---|
4303 | * @access private |
---|
4304 | */ |
---|
4305 | function PRVT_getBytes(&$data, $pos, $num) { |
---|
4306 | |
---|
4307 | // return the integer represented by $num bytes from $pos within $data |
---|
4308 | $ret = 0; |
---|
4309 | |
---|
4310 | for ($i = 0;$i<$num;$i++) { |
---|
4311 | |
---|
4312 | $ret = $ret*256; |
---|
4313 | |
---|
4314 | $ret+= ord($data[$pos+$i]); |
---|
4315 | } |
---|
4316 | |
---|
4317 | return $ret; |
---|
4318 | } |
---|
4319 | |
---|
4320 | |
---|
4321 | /** |
---|
4322 | * add a PNG image into the document, from a file |
---|
4323 | * this should work with remote files |
---|
4324 | */ |
---|
4325 | function addPngFromFile($file, $x, $y, $w = 0, $h = 0) { |
---|
4326 | |
---|
4327 | // read in a png file, interpret it, then add to the system |
---|
4328 | $error = 0; |
---|
4329 | |
---|
4330 | $tmp = get_magic_quotes_runtime(); |
---|
4331 | |
---|
4332 | set_magic_quotes_runtime(0); |
---|
4333 | |
---|
4334 | if ( ($data = file_get_contents($file)) === false) { |
---|
4335 | |
---|
4336 | // $fp = @fopen($file,'rb'); |
---|
4337 | // if ($fp){ |
---|
4338 | // $data = ''; |
---|
4339 | // while(!feof($fp)){ |
---|
4340 | // $data .= fread($fp,1024); |
---|
4341 | // } |
---|
4342 | // fclose($fp); |
---|
4343 | $error = 1; |
---|
4344 | |
---|
4345 | $errormsg = 'trouble opening file: '.$file; |
---|
4346 | } |
---|
4347 | |
---|
4348 | set_magic_quotes_runtime($tmp); |
---|
4349 | |
---|
4350 | |
---|
4351 | if (!$error) { |
---|
4352 | |
---|
4353 | $header = chr(137) .chr(80) .chr(78) .chr(71) .chr(13) .chr(10) .chr(26) .chr(10); |
---|
4354 | |
---|
4355 | if (substr($data, 0, 8) != $header) { |
---|
4356 | |
---|
4357 | $error = 1; |
---|
4358 | |
---|
4359 | $errormsg = 'this file does not have a valid header'; |
---|
4360 | } |
---|
4361 | } |
---|
4362 | |
---|
4363 | |
---|
4364 | if (!$error) { |
---|
4365 | |
---|
4366 | // set pointer |
---|
4367 | $p = 8; |
---|
4368 | |
---|
4369 | $len = strlen($data); |
---|
4370 | |
---|
4371 | // cycle through the file, identifying chunks |
---|
4372 | $haveHeader = 0; |
---|
4373 | |
---|
4374 | $info = array(); |
---|
4375 | |
---|
4376 | $idata = ''; |
---|
4377 | |
---|
4378 | $pdata = ''; |
---|
4379 | |
---|
4380 | while ($p < $len) { |
---|
4381 | |
---|
4382 | $chunkLen = $this->PRVT_getBytes($data, $p, 4); |
---|
4383 | |
---|
4384 | $chunkType = substr($data, $p+4, 4); |
---|
4385 | |
---|
4386 | // echo $chunkType.' - '.$chunkLen.'<br>'; |
---|
4387 | |
---|
4388 | switch ($chunkType) { |
---|
4389 | |
---|
4390 | case 'IHDR': |
---|
4391 | |
---|
4392 | // this is where all the file information comes from |
---|
4393 | $info['width'] = $this->PRVT_getBytes($data, $p+8, 4); |
---|
4394 | |
---|
4395 | $info['height'] = $this->PRVT_getBytes($data, $p+12, 4); |
---|
4396 | |
---|
4397 | $info['bitDepth'] = ord($data[$p+16]); |
---|
4398 | |
---|
4399 | $info['colorType'] = ord($data[$p+17]); |
---|
4400 | |
---|
4401 | $info['compressionMethod'] = ord($data[$p+18]); |
---|
4402 | |
---|
4403 | $info['filterMethod'] = ord($data[$p+19]); |
---|
4404 | |
---|
4405 | $info['interlaceMethod'] = ord($data[$p+20]); |
---|
4406 | |
---|
4407 | //print_r($info); |
---|
4408 | $haveHeader = 1; |
---|
4409 | |
---|
4410 | if ($info['compressionMethod'] != 0) { |
---|
4411 | |
---|
4412 | $error = 1; |
---|
4413 | |
---|
4414 | $errormsg = 'unsupported compression method'; |
---|
4415 | } |
---|
4416 | |
---|
4417 | if ($info['filterMethod'] != 0) { |
---|
4418 | |
---|
4419 | $error = 1; |
---|
4420 | |
---|
4421 | $errormsg = 'unsupported filter method'; |
---|
4422 | } |
---|
4423 | |
---|
4424 | break; |
---|
4425 | |
---|
4426 | case 'PLTE': |
---|
4427 | |
---|
4428 | $pdata.= substr($data, $p+8, $chunkLen); |
---|
4429 | |
---|
4430 | break; |
---|
4431 | |
---|
4432 | case 'IDAT': |
---|
4433 | |
---|
4434 | $idata.= substr($data, $p+8, $chunkLen); |
---|
4435 | |
---|
4436 | break; |
---|
4437 | |
---|
4438 | case 'tRNS': |
---|
4439 | |
---|
4440 | //this chunk can only occur once and it must occur after the PLTE chunk and before IDAT chunk |
---|
4441 | //print "tRNS found, color type = ".$info['colorType']."\n"; |
---|
4442 | $transparency = array(); |
---|
4443 | |
---|
4444 | if ($info['colorType'] == 3) { |
---|
4445 | // indexed color, rbg |
---|
4446 | /* corresponding to entries in the plte chunk |
---|
4447 | Alpha for palette index 0: 1 byte |
---|
4448 | Alpha for palette index 1: 1 byte |
---|
4449 | ...etc... |
---|
4450 | */ |
---|
4451 | // there will be one entry for each palette entry. up until the last non-opaque entry. |
---|
4452 | // set up an array, stretching over all palette entries which will be o (opaque) or 1 (transparent) |
---|
4453 | $transparency['type'] = 'indexed'; |
---|
4454 | |
---|
4455 | $numPalette = strlen($pdata) /3; |
---|
4456 | |
---|
4457 | $trans = 0; |
---|
4458 | |
---|
4459 | for ($i = $chunkLen;$i >= 0;$i--) { |
---|
4460 | |
---|
4461 | if (ord($data[$p+8+$i]) == 0) { |
---|
4462 | |
---|
4463 | $trans = $i; |
---|
4464 | } |
---|
4465 | } |
---|
4466 | |
---|
4467 | $transparency['data'] = $trans; |
---|
4468 | } elseif ($info['colorType'] == 0) { |
---|
4469 | // grayscale |
---|
4470 | /* corresponding to entries in the plte chunk |
---|
4471 | Gray: 2 bytes, range 0 .. (2^bitdepth)-1 |
---|
4472 | */ |
---|
4473 | // $transparency['grayscale'] = $this->PRVT_getBytes($data,$p+8,2); // g = grayscale |
---|
4474 | $transparency['type'] = 'indexed'; |
---|
4475 | |
---|
4476 | $transparency['data'] = ord($data[$p+8+1]); |
---|
4477 | } elseif ($info['colorType'] == 2) { |
---|
4478 | // truecolor |
---|
4479 | /* corresponding to entries in the plte chunk |
---|
4480 | Red: 2 bytes, range 0 .. (2^bitdepth)-1 |
---|
4481 | Green: 2 bytes, range 0 .. (2^bitdepth)-1 |
---|
4482 | Blue: 2 bytes, range 0 .. (2^bitdepth)-1 |
---|
4483 | */ |
---|
4484 | $transparency['r'] = $this->PRVT_getBytes($data, $p+8, 2); |
---|
4485 | // r from truecolor |
---|
4486 | $transparency['g'] = $this->PRVT_getBytes($data, $p+10, 2); |
---|
4487 | // g from truecolor |
---|
4488 | $transparency['b'] = $this->PRVT_getBytes($data, $p+12, 2); |
---|
4489 | // b from truecolor |
---|
4490 | |
---|
4491 | $transparency['type'] = 'color-key'; |
---|
4492 | |
---|
4493 | } else { |
---|
4494 | |
---|
4495 | //unsupported transparency type |
---|
4496 | |
---|
4497 | } |
---|
4498 | |
---|
4499 | // KS End new code |
---|
4500 | break; |
---|
4501 | |
---|
4502 | default: |
---|
4503 | |
---|
4504 | break; |
---|
4505 | } |
---|
4506 | |
---|
4507 | |
---|
4508 | $p+= $chunkLen+12; |
---|
4509 | } |
---|
4510 | |
---|
4511 | |
---|
4512 | if (!$haveHeader) { |
---|
4513 | |
---|
4514 | $error = 1; |
---|
4515 | |
---|
4516 | $errormsg = 'information header is missing'; |
---|
4517 | } |
---|
4518 | |
---|
4519 | if (isset($info['interlaceMethod']) && $info['interlaceMethod']) { |
---|
4520 | |
---|
4521 | $error = 1; |
---|
4522 | |
---|
4523 | $errormsg = 'There appears to be no support for interlaced images in pdf.'; |
---|
4524 | } |
---|
4525 | } |
---|
4526 | |
---|
4527 | |
---|
4528 | if (!$error && $info['bitDepth'] > 8) { |
---|
4529 | |
---|
4530 | $error = 1; |
---|
4531 | |
---|
4532 | $errormsg = 'only bit depth of 8 or less is supported'; |
---|
4533 | } |
---|
4534 | |
---|
4535 | |
---|
4536 | if (!$error) { |
---|
4537 | |
---|
4538 | if ($info['colorType'] != 2 && $info['colorType'] != 0 && $info['colorType'] != 3) { |
---|
4539 | |
---|
4540 | $error = 1; |
---|
4541 | |
---|
4542 | $errormsg = 'transparancey alpha channel not supported, transparency only supported for palette images.'; |
---|
4543 | } else { |
---|
4544 | |
---|
4545 | switch ($info['colorType']) { |
---|
4546 | |
---|
4547 | case 3: |
---|
4548 | |
---|
4549 | $color = 'DeviceRGB'; |
---|
4550 | |
---|
4551 | $ncolor = 1; |
---|
4552 | |
---|
4553 | break; |
---|
4554 | |
---|
4555 | case 2: |
---|
4556 | |
---|
4557 | $color = 'DeviceRGB'; |
---|
4558 | |
---|
4559 | $ncolor = 3; |
---|
4560 | |
---|
4561 | break; |
---|
4562 | |
---|
4563 | case 0: |
---|
4564 | |
---|
4565 | $color = 'DeviceGray'; |
---|
4566 | |
---|
4567 | $ncolor = 1; |
---|
4568 | |
---|
4569 | break; |
---|
4570 | } |
---|
4571 | } |
---|
4572 | } |
---|
4573 | |
---|
4574 | if ($error) { |
---|
4575 | |
---|
4576 | $this->addMessage('PNG error - ('.$file.') '.$errormsg); |
---|
4577 | |
---|
4578 | return; |
---|
4579 | } |
---|
4580 | |
---|
4581 | if ($w == 0) { |
---|
4582 | |
---|
4583 | $w = $h/$info['height']*$info['width']; |
---|
4584 | } |
---|
4585 | |
---|
4586 | if ($h == 0) { |
---|
4587 | |
---|
4588 | $h = $w*$info['height']/$info['width']; |
---|
4589 | } |
---|
4590 | |
---|
4591 | //print_r($info); |
---|
4592 | // so this image is ok... add it in. |
---|
4593 | $this->numImages++; |
---|
4594 | |
---|
4595 | $im = $this->numImages; |
---|
4596 | |
---|
4597 | $label = 'I'.$im; |
---|
4598 | |
---|
4599 | $this->numObj++; |
---|
4600 | |
---|
4601 | // $this->o_image($this->numObj,'new',array('label' => $label,'data' => $idata,'iw' => $w,'ih' => $h,'type' => 'png','ic' => $info['width'])); |
---|
4602 | $options = array('label' => $label, 'data' => $idata, 'bitsPerComponent' => $info['bitDepth'], 'pdata' => $pdata, 'iw' => $info['width'], 'ih' => $info['height'], 'type' => 'png', 'color' => $color, 'ncolor' => $ncolor); |
---|
4603 | |
---|
4604 | if (isset($transparency)) { |
---|
4605 | |
---|
4606 | $options['transparency'] = $transparency; |
---|
4607 | } |
---|
4608 | |
---|
4609 | $this->o_image($this->numObj, 'new', $options); |
---|
4610 | |
---|
4611 | |
---|
4612 | $this->objects[$this->currentContents]['c'].= "\nq"; |
---|
4613 | |
---|
4614 | $this->objects[$this->currentContents]['c'].= "\n".sprintf('%.3f', $w) ." 0 0 ".sprintf('%.3f', $h) ." ".sprintf('%.3f', $x) ." ".sprintf('%.3f', $y) ." cm"; |
---|
4615 | |
---|
4616 | $this->objects[$this->currentContents]['c'].= "\n/".$label.' Do'; |
---|
4617 | |
---|
4618 | $this->objects[$this->currentContents]['c'].= "\nQ"; |
---|
4619 | } |
---|
4620 | |
---|
4621 | |
---|
4622 | /** |
---|
4623 | * add a JPEG image into the document, from a file |
---|
4624 | */ |
---|
4625 | function addJpegFromFile($img, $x, $y, $w = 0, $h = 0) { |
---|
4626 | |
---|
4627 | // attempt to add a jpeg image straight from a file, using no GD commands |
---|
4628 | // note that this function is unable to operate on a remote file. |
---|
4629 | |
---|
4630 | if (!file_exists($img)) { |
---|
4631 | |
---|
4632 | return; |
---|
4633 | } |
---|
4634 | |
---|
4635 | |
---|
4636 | $tmp = getimagesize($img); |
---|
4637 | |
---|
4638 | $imageWidth = $tmp[0]; |
---|
4639 | |
---|
4640 | $imageHeight = $tmp[1]; |
---|
4641 | |
---|
4642 | |
---|
4643 | if (isset($tmp['channels'])) { |
---|
4644 | |
---|
4645 | $channels = $tmp['channels']; |
---|
4646 | } else { |
---|
4647 | |
---|
4648 | $channels = 3; |
---|
4649 | } |
---|
4650 | |
---|
4651 | |
---|
4652 | if ($w <= 0 && $h <= 0) { |
---|
4653 | |
---|
4654 | $w = $imageWidth; |
---|
4655 | } |
---|
4656 | |
---|
4657 | if ($w == 0) { |
---|
4658 | |
---|
4659 | $w = $h/$imageHeight*$imageWidth; |
---|
4660 | } |
---|
4661 | |
---|
4662 | if ($h == 0) { |
---|
4663 | |
---|
4664 | $h = $w*$imageHeight/$imageWidth; |
---|
4665 | } |
---|
4666 | |
---|
4667 | |
---|
4668 | //$fp = fopen($img,'rb'); |
---|
4669 | |
---|
4670 | $tmp = get_magic_quotes_runtime(); |
---|
4671 | |
---|
4672 | set_magic_quotes_runtime(0); |
---|
4673 | |
---|
4674 | $data = file_get_contents($img); |
---|
4675 | |
---|
4676 | //fread($fp,filesize($img)); |
---|
4677 | set_magic_quotes_runtime($tmp); |
---|
4678 | |
---|
4679 | |
---|
4680 | //fclose($fp); |
---|
4681 | |
---|
4682 | $this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight, $channels); |
---|
4683 | } |
---|
4684 | |
---|
4685 | |
---|
4686 | /** |
---|
4687 | * add an image into the document, from a GD object |
---|
4688 | * this function is not all that reliable, and I would probably encourage people to use |
---|
4689 | * the file based functions |
---|
4690 | */ |
---|
4691 | function addImage(&$img, $x, $y, $w = 0, $h = 0, $quality = 75) { |
---|
4692 | |
---|
4693 | // add a new image into the current location, as an external object |
---|
4694 | // add the image at $x,$y, and with width and height as defined by $w & $h |
---|
4695 | |
---|
4696 | // note that this will only work with full colour images and makes them jpg images for display |
---|
4697 | // later versions could present lossless image formats if there is interest. |
---|
4698 | |
---|
4699 | // there seems to be some problem here in that images that have quality set above 75 do not appear |
---|
4700 | // not too sure why this is, but in the meantime I have restricted this to 75. |
---|
4701 | if ($quality>75) { |
---|
4702 | |
---|
4703 | $quality = 75; |
---|
4704 | } |
---|
4705 | |
---|
4706 | |
---|
4707 | // if the width or height are set to zero, then set the other one based on keeping the image |
---|
4708 | // height/width ratio the same, if they are both zero, then give up :) |
---|
4709 | $imageWidth = imagesx($img); |
---|
4710 | |
---|
4711 | $imageHeight = imagesy($img); |
---|
4712 | |
---|
4713 | |
---|
4714 | if ($w <= 0 && $h <= 0) { |
---|
4715 | |
---|
4716 | return; |
---|
4717 | } |
---|
4718 | |
---|
4719 | if ($w == 0) { |
---|
4720 | |
---|
4721 | $w = $h/$imageHeight*$imageWidth; |
---|
4722 | } |
---|
4723 | |
---|
4724 | if ($h == 0) { |
---|
4725 | |
---|
4726 | $h = $w*$imageHeight/$imageWidth; |
---|
4727 | } |
---|
4728 | |
---|
4729 | |
---|
4730 | // gotta get the data out of the img.. |
---|
4731 | |
---|
4732 | // so I write to a temp file, and then read it back.. soo ugly, my apologies. |
---|
4733 | $tmpDir = '/tmp'; |
---|
4734 | |
---|
4735 | $tmpName = tempnam($tmpDir, 'img'); |
---|
4736 | |
---|
4737 | imagejpeg($img, $tmpName, $quality); |
---|
4738 | |
---|
4739 | //$fp = fopen($tmpName,'rb'); |
---|
4740 | |
---|
4741 | $tmp = get_magic_quotes_runtime(); |
---|
4742 | |
---|
4743 | set_magic_quotes_runtime(0); |
---|
4744 | |
---|
4745 | if ( ($data = file_get_contents($tmpName)) === false) { |
---|
4746 | |
---|
4747 | // $fp = @fopen($tmpName,'rb'); |
---|
4748 | // if ($fp){ |
---|
4749 | // $data = ''; |
---|
4750 | // while(!feof($fp)){ |
---|
4751 | // $data .= fread($fp,1024); |
---|
4752 | // } |
---|
4753 | // fclose($fp); |
---|
4754 | $error = 1; |
---|
4755 | |
---|
4756 | $errormsg = 'trouble opening file'; |
---|
4757 | } |
---|
4758 | |
---|
4759 | // $data = fread($fp,filesize($tmpName)); |
---|
4760 | set_magic_quotes_runtime($tmp); |
---|
4761 | |
---|
4762 | // fclose($fp); |
---|
4763 | unlink($tmpName); |
---|
4764 | |
---|
4765 | $this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight); |
---|
4766 | } |
---|
4767 | |
---|
4768 | |
---|
4769 | /** |
---|
4770 | * common code used by the two JPEG adding functions |
---|
4771 | * |
---|
4772 | * @access private |
---|
4773 | */ |
---|
4774 | function addJpegImage_common(&$data, $x, $y, $w = 0, $h = 0, $imageWidth, $imageHeight, $channels = 3) { |
---|
4775 | |
---|
4776 | // note that this function is not to be called externally |
---|
4777 | // it is just the common code between the GD and the file options |
---|
4778 | $this->numImages++; |
---|
4779 | |
---|
4780 | $im = $this->numImages; |
---|
4781 | |
---|
4782 | $label = 'I'.$im; |
---|
4783 | |
---|
4784 | $this->numObj++; |
---|
4785 | |
---|
4786 | $this->o_image($this->numObj, 'new', array('label' => $label, 'data' => &$data, 'iw' => $imageWidth, 'ih' => $imageHeight, 'channels' => $channels)); |
---|
4787 | |
---|
4788 | |
---|
4789 | $this->objects[$this->currentContents]['c'].= "\nq"; |
---|
4790 | |
---|
4791 | $this->objects[$this->currentContents]['c'].= "\n".sprintf('%.3f', $w) ." 0 0 ".sprintf('%.3f', $h) ." ".sprintf('%.3f', $x) ." ".sprintf('%.3f', $y) ." cm"; |
---|
4792 | |
---|
4793 | $this->objects[$this->currentContents]['c'].= "\n/".$label.' Do'; |
---|
4794 | |
---|
4795 | $this->objects[$this->currentContents]['c'].= "\nQ"; |
---|
4796 | } |
---|
4797 | |
---|
4798 | |
---|
4799 | /** |
---|
4800 | * specify where the document should open when it first starts |
---|
4801 | */ |
---|
4802 | function openHere($style, $a = 0, $b = 0, $c = 0) { |
---|
4803 | |
---|
4804 | // this function will open the document at a specified page, in a specified style |
---|
4805 | // the values for style, and the required paramters are: |
---|
4806 | // 'XYZ' left, top, zoom |
---|
4807 | // 'Fit' |
---|
4808 | // 'FitH' top |
---|
4809 | // 'FitV' left |
---|
4810 | // 'FitR' left,bottom,right |
---|
4811 | // 'FitB' |
---|
4812 | // 'FitBH' top |
---|
4813 | // 'FitBV' left |
---|
4814 | $this->numObj++; |
---|
4815 | |
---|
4816 | $this->o_destination($this->numObj, 'new', array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c)); |
---|
4817 | |
---|
4818 | $id = $this->catalogId; |
---|
4819 | |
---|
4820 | $this->o_catalog($id, 'openHere', $this->numObj); |
---|
4821 | } |
---|
4822 | |
---|
4823 | |
---|
4824 | /** |
---|
4825 | * create a labelled destination within the document |
---|
4826 | */ |
---|
4827 | function addDestination($label, $style, $a = 0, $b = 0, $c = 0) { |
---|
4828 | |
---|
4829 | // associates the given label with the destination, it is done this way so that a destination can be specified after |
---|
4830 | // it has been linked to |
---|
4831 | // styles are the same as the 'openHere' function |
---|
4832 | $this->numObj++; |
---|
4833 | |
---|
4834 | $this->o_destination($this->numObj, 'new', array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c)); |
---|
4835 | |
---|
4836 | $id = $this->numObj; |
---|
4837 | |
---|
4838 | // store the label->idf relationship, note that this means that labels can be used only once |
---|
4839 | $this->destinations["$label"] = $id; |
---|
4840 | } |
---|
4841 | |
---|
4842 | |
---|
4843 | /** |
---|
4844 | * define font families, this is used to initialize the font families for the default fonts |
---|
4845 | * and for the user to add new ones for their fonts. The default bahavious can be overridden should |
---|
4846 | * that be desired. |
---|
4847 | */ |
---|
4848 | function setFontFamily($family, $options = '') { |
---|
4849 | |
---|
4850 | if (!is_array($options)) { |
---|
4851 | |
---|
4852 | if ($family == 'init') { |
---|
4853 | |
---|
4854 | // set the known family groups |
---|
4855 | // these font families will be used to enable bold and italic markers to be included |
---|
4856 | // within text streams. html forms will be used... <b></b> <i></i> |
---|
4857 | $this->fontFamilies['Helvetica.afm'] = |
---|
4858 | array('b' => 'Helvetica-Bold.afm', |
---|
4859 | 'i' => 'Helvetica-Oblique.afm', |
---|
4860 | 'bi' => 'Helvetica-BoldOblique.afm', |
---|
4861 | 'ib' => 'Helvetica-BoldOblique.afm'); |
---|
4862 | |
---|
4863 | $this->fontFamilies['Courier.afm'] = |
---|
4864 | array('b' => 'Courier-Bold.afm', |
---|
4865 | 'i' => 'Courier-Oblique.afm', |
---|
4866 | 'bi' => 'Courier-BoldOblique.afm', |
---|
4867 | 'ib' => 'Courier-BoldOblique.afm'); |
---|
4868 | |
---|
4869 | $this->fontFamilies['Times-Roman.afm'] = |
---|
4870 | array('b' => 'Times-Bold.afm', |
---|
4871 | 'i' => 'Times-Italic.afm', |
---|
4872 | 'bi' => 'Times-BoldItalic.afm', |
---|
4873 | 'ib' => 'Times-BoldItalic.afm'); |
---|
4874 | } |
---|
4875 | } else { |
---|
4876 | |
---|
4877 | // the user is trying to set a font family |
---|
4878 | // note that this can also be used to set the base ones to something else |
---|
4879 | if (strlen($family)) { |
---|
4880 | |
---|
4881 | $this->fontFamilies[$family] = $options; |
---|
4882 | } |
---|
4883 | } |
---|
4884 | } |
---|
4885 | |
---|
4886 | |
---|
4887 | /** |
---|
4888 | * used to add messages for use in debugging |
---|
4889 | */ |
---|
4890 | function addMessage($message) { |
---|
4891 | |
---|
4892 | $this->messages.= $message."\n"; |
---|
4893 | } |
---|
4894 | |
---|
4895 | |
---|
4896 | /** |
---|
4897 | * a few functions which should allow the document to be treated transactionally. |
---|
4898 | */ |
---|
4899 | function transaction($action) { |
---|
4900 | |
---|
4901 | switch ($action) { |
---|
4902 | |
---|
4903 | case 'start': |
---|
4904 | |
---|
4905 | // store all the data away into the checkpoint variable |
---|
4906 | $data = get_object_vars($this); |
---|
4907 | |
---|
4908 | $this->checkpoint = $data; |
---|
4909 | |
---|
4910 | unset($data); |
---|
4911 | |
---|
4912 | break; |
---|
4913 | |
---|
4914 | case 'commit': |
---|
4915 | |
---|
4916 | if (is_array($this->checkpoint) && isset($this->checkpoint['checkpoint'])) { |
---|
4917 | |
---|
4918 | $tmp = $this->checkpoint['checkpoint']; |
---|
4919 | |
---|
4920 | $this->checkpoint = $tmp; |
---|
4921 | |
---|
4922 | unset($tmp); |
---|
4923 | } else { |
---|
4924 | |
---|
4925 | $this->checkpoint = ''; |
---|
4926 | } |
---|
4927 | |
---|
4928 | break; |
---|
4929 | |
---|
4930 | case 'rewind': |
---|
4931 | |
---|
4932 | // do not destroy the current checkpoint, but move us back to the state then, so that we can try again |
---|
4933 | if (is_array($this->checkpoint)) { |
---|
4934 | |
---|
4935 | // can only abort if were inside a checkpoint |
---|
4936 | $tmp = $this->checkpoint; |
---|
4937 | |
---|
4938 | foreach ($tmp as $k => $v) { |
---|
4939 | |
---|
4940 | if ($k != 'checkpoint') { |
---|
4941 | |
---|
4942 | $this->$k = $v; |
---|
4943 | } |
---|
4944 | } |
---|
4945 | |
---|
4946 | unset($tmp); |
---|
4947 | } |
---|
4948 | |
---|
4949 | break; |
---|
4950 | |
---|
4951 | case 'abort': |
---|
4952 | |
---|
4953 | if (is_array($this->checkpoint)) { |
---|
4954 | |
---|
4955 | // can only abort if were inside a checkpoint |
---|
4956 | $tmp = $this->checkpoint; |
---|
4957 | |
---|
4958 | foreach ($tmp as $k => $v) { |
---|
4959 | |
---|
4960 | $this->$k = $v; |
---|
4961 | } |
---|
4962 | |
---|
4963 | unset($tmp); |
---|
4964 | } |
---|
4965 | |
---|
4966 | break; |
---|
4967 | } |
---|
4968 | } |
---|
4969 | } |
---|
4970 | // end of class |
---|
4971 | |
---|
4972 | ?> |
---|