Exponential 6.0.13 Exponential Enterprise Open Source CMS - PHP Class Reference

class.pdf.php
Go to the documentation of this file.
1 <?php
17 class Cpdf
18 {
19
20 public $ez;
24 public $nCallBack;
28 public $numObj = 0;
29
33 public $objects = array();
34
38 public $catalogId;
39
44 public $fonts = array();
45
49 public $currentFont = '';
50
54 public $currentBaseFont = '';
55
59 public $currentFontNum = 0;
60
64 public $currentNode;
65
69 public $currentPage;
70
74 public $currentContents;
75
79 public $numFonts = 0;
80
84 public $currentColour;
85
89 public $currentStrokeColour;
90
94 public $currentLineStyle = '';
95
100 public $stateStack = array();
101
105 public $nStateStack = 0;
106
110 public $numPages = 0;
111
115 public $stack = array();
116
120 public $nStack = 0;
121
126 public $looseObjects = array();
127
131 public $addLooseObjects = array();
132
137 public $infoObject = 0;
138
142 public $numImages = 0;
143
148 public $options = array( 'compression' => 1 );
149
153 public $firstPageId;
154
159 public $wordSpaceAdjust = 0;
160
164 public $procsetObjectId;
165
171 public $fontFamilies = array();
172
176 public $currentTextState = '';
177
181 public $messages = '';
182
186 public $arc4 = '';
187
191 public $arc4_objnum = 0;
192
196 public $fileIdentifier = '';
197
201 public $encrypted = 0;
202
206 public $encryptionKey = '';
207
211 public $callback = array();
212
216 public $nCallback = 0;
217
222 public $destinations = array();
223
229 public $checkpoint = '';
230
236 public function __construct( $pageSize = array( 0, 0, 612, 792 ) )
237 {
238 $this->currentColour = eZMath::rgbToCMYK( array( 'r' => -1,
239 'g' => -1,
240 'b' => -1 ) );
241 $this->currentStrokeColour = eZMath::rgbToCMYK( array( 'r' => -1,
242 'g' => -1,
243 'b' => -1 ) );
244
245 $this->newDocument( $pageSize );
246
247 // also initialize the font families that are known about already
248 $this->setFontFamily( 'init' );
249 // $this->fileIdentifier = md5('xxxxxxxx'.time());
250
251 }
252
271 function o_destination( $id, $action, $options = '' )
272 {
273 if ( $action != 'new' )
274 {
275 $o =& $this->objects[$id];
276 }
277 switch ( $action )
278 {
279 case 'new':
280 $this->objects[$id] = array( 't' => 'destination', 'info' => array() );
281 $tmp = '';
282 switch ( $options['type'] )
283 {
284 case 'XYZ':
285 case 'FitR':
286 $tmp = ' ' . sprintf( '%.3F', $options['p3'] ) . $tmp;
287 case 'FitV':
288 case 'FitBH':
289 case 'FitBV':
290 $tmp = ' ' . sprintf( '%.3F', $options['p2'] ) . $tmp;
291 case 'Fit':
292 case 'FitH':
293 $tmp = ' ' . sprintf( '%.3F', $options['p1'] ) . $tmp;
294 case 'FitB':
295 $tmp = $options['type'] . $tmp;
296 $this->objects[$id]['info']['string'] = $tmp;
297 $this->objects[$id]['info']['page'] = $options['page'];
298 }break;
299
300 case 'out':
301 $tmp = $o['info'];
302 $res = "\n" . $id . " 0 obj\n" . '[' . $tmp['page'] . ' 0 R /' . $tmp['string'] . "]\nendobj\n";
303 return $res;
304 break;
305 }
306 }
307
311 function o_viewerPreferences( $id, $action, $options = '' )
312 {
313 if ( $action != 'new' )
314 {
315 $o =& $this->objects[$id];
316 }
317 switch ( $action )
318 {
319 case 'new':
320 $this->objects[$id] = array( 't' => 'viewerPreferences', 'info' => array() );
321 break;
322 case 'add':
323 foreach ( $options as $k => $v )
324 {
325 switch ( $k )
326 {
327 case 'HideToolbar':
328 case 'HideMenubar':
329 case 'HideWindowUI':
330 case 'FitWindow':
331 case 'CenterWindow':
332 case 'NonFullScreenPageMode':
333 case 'Direction':
334 $o['info'][$k] = $v;
335 break;
336 }
337 }break;
338 case 'out':
339 $res = "\n" . $id . " 0 obj\n" . '<< ';
340 foreach($o['info'] as $k=>$v)
341 {
342 $res .= "\n/" . $k . ' ' . $v;
343 }
344 $res .= "\n>>\n";
345 return $res;
346 break;
347 }
348 }
349
353 function o_catalog( $id, $action, $options = '' )
354 {
355 if ( $action != 'new' )
356 {
357 $o =& $this->objects[$id];
358 }
359 switch ( $action )
360 {
361 case 'new':
362 $this->objects[$id] = array( 't' => 'catalog', 'info' => array() );
363 $this->catalogId = $id;
364 break;
365 case 'outlines':
366 case 'pages':
367 case 'openHere':
368 $o['info'][$action] = $options;
369 break;
370 case 'viewerPreferences':
371 if ( !isset( $o['info']['viewerPreferences'] ) )
372 {
373 $this->numObj++;
374 $this->o_viewerPreferences( $this->numObj, 'new' );
375 $o['info']['viewerPreferences'] = $this->numObj;
376 }
377 $vp = $o['info']['viewerPreferences'];
378 $this->o_viewerPreferences( $vp, 'add', $options );
379 break;
380 case 'out':
381 $res= "\n" . $id . " 0 obj\n" . '<< /Type /Catalog';
382 foreach ( $o['info'] as $k => $v )
383 {
384 switch ( $k )
385 {
386 case 'outlines':
387 $res .= "\n" . '/Outlines ' . $v . ' 0 R';
388 break;
389 case 'pages':
390 $res .= "\n" . '/Pages ' . $v . ' 0 R';
391 break;
392 case 'viewerPreferences':
393 $res .= "\n" . '/ViewerPreferences ' . $o['info']['viewerPreferences'] . ' 0 R';
394 break;
395 case 'openHere':
396 $res .= "\n" . '/OpenAction ' . $o['info']['openHere'] . ' 0 R';
397 break;
398 }
399 }
400 $res .= " >>\nendobj";
401 return $res;
402 break;
403 }
404 }
405
409 function o_pages( $id, $action, $options = '' )
410 {
411 if ($action!='new')
412 {
413 $o =& $this->objects[$id];
414 }
415 switch ( $action )
416 {
417 case 'new':
418 $this->objects[$id] = array( 't' => 'pages', 'info' => array() );
419 $this->o_catalog( $this->catalogId, 'pages', $id );
420 break;
421 case 'page':
422 if ( !is_array( $options ) )
423 {
424 // then it will just be the id of the new page
425 $o['info']['pages'][] = $options;
426 }
427 else
428 {
429 // then it should be an array having 'id','rid','pos', where rid=the page to which this one will be placed relative
430 // and pos is either 'before' or 'after', saying where this page will fit.
431 if ( isset( $options['id'] ) && isset( $options['rid'] ) && isset( $options['pos'] ) )
432 {
433 $i = array_search( $options['rid'], $o['info']['pages'] );
434 if ( isset( $o['info']['pages'][$i] ) && $o['info']['pages'][$i] == $options['rid'])
435 {
436 // then there is a match
437 // make a space
438 switch ( $options['pos'] )
439 {
440 case 'before':
441 $k = $i;
442 break;
443 case 'after':
444 $k = $i + 1;
445 break;
446 default:
447 $k = -1;
448 break;
449 }
450 if ( $k >= 0 )
451 {
452 for ( $j = count( $o['info']['pages'] ) - 1; $j >= $k; $j-- )
453 {
454 $o['info']['pages'][$j+1] = $o['info']['pages'][$j];
455 }
456 $o['info']['pages'][$k] = $options['id'];
457 }
458 }
459 }
460 }break;
461
462 case 'procset':
463 $o['info']['procset'] = $options;
464 break;
465 case 'mediaBox':
466 $o['info']['mediaBox'] = $options; // which should be an array of 4 numbers
467 break;
468 case 'font':
469 $o['info']['fonts'][] = array( 'objNum' => $options['objNum'], 'fontNum' => $options['fontNum'] );
470 break;
471 case 'shading':
472 $o['info']['shading'][] = array( 'objNum' => $options['objNum'], 'label' => $options['label'] );
473 break;
474 case 'xObject':
475 $o['info']['xObjects'][] = array( 'objNum' => $options['objNum'], 'label' => $options['label'] );
476 break;
477 case 'out':
478 if ( count( $o['info']['pages'] ) )
479 {
480 $res = "\n" . $id . " 0 obj\n<< /Type /Pages\n/Kids [";
481 foreach ( $o['info']['pages'] as $k => $v )
482 {
483 $res .= $v . " 0 R\n";
484 }
485 $res .= "]\n/Count " . count( $this->objects[$id]['info']['pages'] );
486 if ( ( isset( $o['info']['fonts'] ) && count( $o['info']['fonts'] ) ) || isset( $o['info']['procset'] ) )
487 {
488 $res .= "\n/Resources <<";
489 if ( isset( $o['info']['procset'] ) )
490 {
491 $res .= "\n/ProcSet " . $o['info']['procset'] . " 0 R";
492 }
493 if ( isset( $o['info']['fonts'] ) && count( $o['info']['fonts'] ) )
494 {
495 $res .= "\n/Font << ";
496 foreach ( $o['info']['fonts'] as $finfo )
497 {
498 $res .= "\n/F" . $finfo['fontNum'] . " " . $finfo['objNum'] . " 0 R";
499 }
500 $res .= " >>";
501 }
502
503 if ( isset( $o['info']['shading'] ) && count( $o['info']['shading'] ) )
504 {
505 $res .= "\n/Shading <<";
506 foreach ( $o['info']['shading'] as $finfo )
507 {
508 $res .= "\n/" . $finfo['label'] . " " . $finfo['objNum'] . " 0 R";
509 }
510 $res .= " >>";
511 }
512
513 if ( isset( $o['info']['xObjects'] ) && count( $o['info']['xObjects'] ) )
514 {
515 $res .= "\n/XObject << ";
516 foreach ( $o['info']['xObjects'] as $finfo )
517 {
518 $res .= "\n/" . $finfo['label'] . " " . $finfo['objNum'] . " 0 R";
519 }
520 $res .= " >>";
521 }
522 $res .= "\n>>";
523 if ( isset( $o['info']['mediaBox'] ) )
524 {
525 $tmp = $o['info']['mediaBox'];
526 $res .= "\n/MediaBox [" . sprintf( '%.3F', $tmp[0] ) . ' ' . sprintf( '%.3F', $tmp[1] ) . ' ' . sprintf( '%.3F', $tmp[2] ) . ' ' . sprintf( '%.3F', $tmp[3] ) . ']';
527 }
528 }
529 $res.="\n >>\nendobj";
530 }
531 else
532 {
533 $res = "\n" . $id . " 0 obj\n<< /Type /Pages\n/Count 0\n>>\nendobj";
534 }
535 return $res;
536 break;
537 }
538 }
539
543 function o_outlines( $id, $action, $options = '' )
544 {
545 if ( $action != 'new' )
546 {
547 $o =& $this->objects[$id];
548 }
549 switch ( $action )
550 {
551 case 'new':
552 $this->objects[$id] = array( 't' => 'outlines', 'info' => array( 'outlines' => array() ) );
553 $this->o_catalog( $this->catalogId, 'outlines', $id );
554 break;
555 case 'outline':
556 $o['info']['outlines'][] = $options;
557 break;
558 case 'out':
559 if ( count( $o['info']['outlines'] ) )
560 {
561 $res = "\n" . $id . " 0 obj\n<< /Type /Outlines /Kids [";
562 foreach ( $o['info']['outlines'] as $k => $v )
563 {
564 $res .= $v . " 0 R ";
565 }
566 $res .= "] /Count " . count( $o['info']['outlines'] ) . " >>\nendobj";
567 }
568 else
569 {
570 $res = "\n" . $id . " 0 obj\n<< /Type /Outlines /Count 0 >>\nendobj";
571 }
572 return $res;
573 break;
574 }
575 }
576
585 function o_function( $id, $action, $options = '' )
586 {
587 if ( $action != 'new' )
588 {
589 $o =& $this->objects[$id];
590 }
591
592 switch ( $action )
593 {
594 case 'new':
595 {
596 $this->objects[$id] = array ( 't' => 'function',
597 'info' => array() );
598
599 if ( isset( $options['type'] ) ) // Set function type
600 {
601 $this->objects[$id]['info']['type'] = $options['type'];
602 }
603 else
604 {
605 $this->objects[$id]['info']['type'] = '2';
606 }
607
608 if ( isset( $options['Domain'] ) ) // Set function Domain
609 {
610 $this->objects[$id]['info']['Domain'] = $options['Domain'];
611 }
612 else
613 {
614 $this->objects[$id]['info']['Domain'] = '[0.0 1.0]';
615 }
616
617 if ( $this->objects[$id]['info']['type'] == '2' ) // set start and en values
618 {
619 if ( isset( $options['C0'] ) ) // Set values0
620 {
621 $this->objects[$id]['info']['C0'] = $options['C0'];
622 }
623
624 if ( isset( $options['C1'] ) ) // Set values1
625 {
626 $this->objects[$id]['info']['C1'] = $options['C1'];
627 }
628
629 if ( isset( $options['N'] ) ) // set exponent
630 {
631 $this->objects[$id]['info']['N'] = $options['N'];
632 }
633 else
634 {
635 $this->objects[$id]['info']['N'] = '1.0';
636 }
637 }
638
639 } break;
640
641 case 'out':
642 {
643 $res = "\n" . $id . " 0 obj\n<< ";
644 $res.= "/FunctionType " . $o['info']['type'] . "\n";
645 $res.= "/Domain " . $o['info']['Domain'] . "\n";
646 if ( isset ( $o['info']['C0'] ) )
647 $res.= "/C0 " . $o['info']['C0'] . "\n";
648 if ( isset ( $o['info']['C1'] ) )
649 $res.= "/C1 " . $o['info']['C1'] . "\n";
650 $res.= "/N " . $o['info']['N'] . "\n";
651 $res.= ">>\nendobject";
652 return $res;
653 } break;
654 }
655 }
656
667 function o_pattern( $id, $action, $options = '' )
668 {
669 if ( $action != 'new' )
670 {
671 $o =& $this->objects[$id];
672 }
673
674 switch ( $action )
675 {
676 case 'new':
677 {
678 $this->objects[$id] = array( 't' => 'shading',
679 'info' => array() );
680
681 if ( isset( $options['type'] ) ) //Set pattern type
682 {
683 $this->objects[$id]['info']['PatternType'] = $options['type'];
684 }
685 else
686 {
687 $this->objects[$id]['info']['PatternType'] = '2';
688 }
689
690 if ( isset( $options['matrix'] ) )
691 {
692 $this->objects[$id]['info']['Matrix'] = '[ ';
693 foreach ( $options['matrix'] as $coord )
694 {
695 $this->objects[$id]['info']['Matrix'] .= sprintf( ' %.3F ', $coord );
696 }
697 $this->objects[$id]['info']['Matrix'] .= ' ]';
698 }
699
700 if ( $this->objects[$id]['info']['PatternType'] == 1 ) // Pattern type 1, tiling pattern
701 {
702 $this->objects[$id]['info']['PaintType'] = $options['paintType'];
703 $this->objects[$id]['info']['TilingType'] = $options['tilingType'];
704
705 $this->objects[$id]['info']['BBox'] = '[ ';
706 foreach ( $options['bbox'] as $coord )
707 {
708 $this->objects[$id]['info']['BBox'] .= sprintf( ' %.3F ', $coord );
709 }
710 $this->objects[$id]['info']['BBox'] .= ']';
711
712 $this->objects[$id]['info']['XStep'] = $options['xstep'];
713 $this->objects[$id]['info']['YStep'] = $options['ystep'];
714 }
715 else // Non tiling, smooth pattern.
716 {
717 if ( isset( $options['shading'] ) )
718 {
719 $this->objects[$id]['info']['Shading'] = $options['shading'] . ' R';
720 }
721 }
722 } break;
723
724 case 'out':
725 {
726 $res = "\n" . $id . " 0 obj\n<< ";
727 foreach ( $this->objects[$id]['info'] as $key => $info )
728 {
729 $res .= '/' . $key . ' ' . $info[$key] . "\n";
730 }
731 $res .= ">>\nendobject";
732 return $res;
733 } break;
734 }
735 } //TODO: Add pattern too
736
737
748 function o_shading( $id, $action, $options = '' )
749 {
750 if ( $action != 'new' )
751 {
752 $o =& $this->objects[$id];
753 }
754
755 switch ( $action )
756 {
757 case 'new':
758 {
759 $this->objects[$id] = array( 't' => 'shading',
760 'info' => array( 'type' => '2',
761 'ColorSpace' => '/DeviceCMYK' ) );
762
763 if ( !isset( $options['orientation'] ) )
764 {
765 $options['orientation'] = 'vertical';
766 }
767
768 if ( $options['orientation'] == 'horizontal' )
769 {
770 $angle = 0;
771 }
772 else if ( $options['orientation'] == 'vertical' )
773 {
774 $angle = deg2rad( 90 );
775 }
776 else // orientation is an angle
777 {
778 $angle = $options['orientation'];
779 }
780
781 $this->objects[$id]['info']['Coords'] = '[ '. // compute angle of gradient chading
782 sprintf( '%.3F', $options['size']['x1'] ) . ' ' .
783 sprintf( '%.3F', $options['size']['y1'] + $options['size']['height'] ) . ' ' .
784 sprintf( '%.3F', $options['size']['x1'] + $options['size']['width'] * cos( $angle ) ) . ' ' .
785 sprintf( '%.3F', $options['size']['y1'] + $options['size']['height'] * ( 1 - sin( $angle ) ) ) . ']';
786
787 //$this->objects[$id]['info']['Coords'] = '[0.0 0.0 50.0 50.0]';
788
789 if ( is_array( $options['color0'] ) ) //Set chading colors
790 {
791 if ( count( $options['color0'] ) == 3 )
792 {
793 $options['color0'] = eZMath::rgbToCMYK( $options['color0'] );
794 }
795 $color0 = array ( 'c' => sprintf( '%.3F', $options['color0']['c'] ),
796 'm' => sprintf( '%.3F', $options['color0']['m'] ),
797 'y' => sprintf( '%.3F', $options['color0']['y'] ),
798 'k' => sprintf( '%.3F', $options['color0']['k'] ) );
799 }
800 else
801 {
802 $color0 = eZMath::rgbToCMYK2( 0, 0, 0 );
803 }
804 if ( is_array( $options['color1'] ) )
805 {
806 if ( count( $options['color1'] ) == 3 )
807 {
808 $options['color1'] = eZMath::rgbToCMYK( $options['color1'] );
809 }
810 $color1 = array ( 'c' => sprintf( '%.3F', $options['color1']['c'] ),
811 'm' => sprintf( '%.3F', $options['color1']['m'] ),
812 'y' => sprintf( '%.3F', $options['color1']['y'] ),
813 'k' => sprintf( '%.3F', $options['color1']['k'] ) );
814 }
815 else
816 {
817 $color1 = eZMath::rgbToCMYK2( 1, 1, 1 );
818 }
819
820 $this->numObj++;
821 $this->objects[$id]['info']['Function'] = $this->numObj;
822 $this->o_function( $this->numObj, 'new', array ( 'type' => '2',
823 'Domain' => '[0.0 1.0]',
824 'C0' => '['.$color0['c'].' '.$color0['m'].' '.$color0['y'].' '.$color0['k'].']',
825 'C1' => '['.$color1['c'].' '.$color1['m'].' '.$color1['y'].' '.$color1['k'].']',
826 'N' => '1.0' ) );
827 $this->objects[$id]['info']['Extend'] = '[true true]';
828 // also tell the pages node about the new shading
829 $this->o_pages( $this->currentNode, 'shading', array( 'label' => 'Sh' . $id,
830 'objNum'=> $id ) );
831 return 'Sh' . $id;
832 } break;
833
834 case 'out':
835 {
836 $res = "\n" . $id . " 0 obj\n<< ";
837 $res.= "/ShadingType " . $o['info']['type'] . "\n";
838 $res.= "/ColorSpace " . $o['info']['ColorSpace'] . "\n";
839 $res.= "/Coords " . $o['info']['Coords'] . "\n";
840 $res.= "/Function " . $o['info']['Function'] . " 0 R\n";
841 $res.= "/Extend " . $o['info']['Extend'] . "\n";
842 $res.= ">>\nendobject";
843 return $res;
844 } break;
845 }
846 }
847
851 function o_font( $id, $action, $options = '' )
852 {
853 if ( $action != 'new' )
854 {
855 $o =& $this->objects[$id];
856 }
857 switch ( $action )
858 {
859 case 'new':
860 $this->objects[$id] = array( 't' => 'font',
861 'info' => array( 'name' => $options['name'],
862 'SubType' => 'Type1' ) );
863 $fontNum = $this->numFonts;
864 $this->objects[$id]['info']['fontNum'] = $fontNum;
865 // deal with the encoding and the differences
866 if ( isset( $options['differences'] ) )
867 {
868 // then we'll need an encoding dictionary
869 $this->numObj++;
870 $this->o_fontEncoding( $this->numObj, 'new', $options );
871 $this->objects[$id]['info']['encodingDictionary'] = $this->numObj;
872 }
873 else if ( isset( $options['encoding'] ) )
874 {
875 // we can specify encoding here
876 switch ( $options['encoding'] )
877 {
878 case 'WinAnsiEncoding':
879 case 'MacRomanEncoding':
880 case 'MacExpertEncoding':
881 $this->objects[$id]['info']['encoding'] = $options['encoding'];
882 break;
883 case 'none':
884 break;
885 default:
886 $this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
887 break;
888 }
889 }
890 else
891 {
892 $this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
893 }
894 // also tell the pages node about the new font
895 $this->o_pages( $this->currentNode, 'font', array( 'fontNum' => $fontNum, 'objNum' => $id ) );
896 break;
897
898 case 'add':
899 foreach ( $options as $k => $v )
900 {
901 switch ( $k )
902 {
903 case 'BaseFont':
904 $o['info']['name'] = $v;
905 break;
906 case 'FirstChar':
907 case 'LastChar':
908 case 'Widths':
909 case 'FontDescriptor':
910 case 'SubType':
911 $this->addMessage( 'o_font ' . $k . " : " . $v );
912 $o['info'][$k] = $v;
913 break;
914 }
915 }
916 break;
917
918 case 'out':
919 $res = "\n" . $id . " 0 obj\n<< /Type /Font\n/Subtype /" . $o['info']['SubType'] . "\n";
920 $res.= "/Name /F" . $o['info']['fontNum'] . "\n";
921 $res.= "/BaseFont /" . $o['info']['name'] . "\n";
922 if ( isset( $o['info']['encodingDictionary'] ) )
923 {
924 // then place a reference to the dictionary
925 $res.= "/Encoding " . $o['info']['encodingDictionary'] . " 0 R\n";
926 }
927 else if ( isset( $o['info']['encoding'] ) )
928 {
929 // use the specified encoding
930 $res.= "/Encoding /" . $o['info']['encoding'] . "\n";
931 }
932 if ( isset($o['info']['FirstChar'] ) )
933 {
934 $res.= "/FirstChar " . $o['info']['FirstChar'] . "\n";
935 }
936 if ( isset( $o['info']['LastChar'] ) )
937 {
938 $res.= "/LastChar " . $o['info']['LastChar'] . "\n";
939 }
940 if ( isset( $o['info']['Widths'] ) )
941 {
942 $res.= "/Widths " . $o['info']['Widths'] . " 0 R\n";
943 }
944 if ( isset( $o['info']['FontDescriptor'] ) )
945 {
946 $res.= "/FontDescriptor " . $o['info']['FontDescriptor'] . " 0 R\n";
947 }
948 $res.= ">>\nendobj";
949 return $res;
950 break;
951 }
952 }
953
957 function o_fontDescriptor( $id, $action, $options = '')
958 {
959 if ( $action != 'new' )
960 {
961 $o =& $this->objects[$id];
962 }
963 switch ( $action )
964 {
965 case 'new':
966 $this->objects[$id] = array( 't' => 'fontDescriptor', 'info' => $options );
967 break;
968 case 'out':
969 $res = "\n" . $id . " 0 obj\n<< /Type /FontDescriptor\n";
970 foreach ( $o['info'] as $label => $value )
971 {
972 switch ( $label )
973 {
974 case 'Ascent':
975 case 'CapHeight':
976 case 'Descent':
977 case 'Flags':
978 case 'ItalicAngle':
979 case 'StemV':
980 case 'AvgWidth':
981 case 'Leading':
982 case 'MaxWidth':
983 case 'MissingWidth':
984 case 'StemH':
985 case 'XHeight':
986 case 'CharSet':
987 if ( strlen( $value ) )
988 {
989 $res.= '/'.$label.' '.$value."\n";
990 }
991 break;
992 case 'FontFile':
993 case 'FontFile2':
994 case 'FontFile3':
995 $res.= '/'.$label.' '.$value." 0 R\n";
996 break;
997 case 'FontBBox':
998 $res.= '/'.$label.' ['.$value[0].' '.$value[1].' '.$value[2].' '.$value[3]."]\n";
999 break;
1000 case 'FontName':
1001 $res.= '/'.$label.' /'.$value."\n";
1002 break;
1003 }
1004 }
1005 $res .= ">>\nendobj";
1006 return $res;
1007 break;
1008 }
1009 }
1010
1014 function o_fontEncoding( $id, $action, $options = '' )
1015 {
1016 if ( $action != 'new' )
1017 {
1018 $o =& $this->objects[$id];
1019 }
1020 switch ( $action )
1021 {
1022 case 'new':
1023 // the options array should contain 'differences' and maybe 'encoding'
1024 $this->objects[$id] = array( 't' => 'fontEncoding',
1025 'info' => $options );
1026 break;
1027 case 'out':
1028 $res = "\n" . $id . " 0 obj\n<< /Type /Encoding\n";
1029 if ( !isset( $o['info']['encoding'] ) )
1030 {
1031 $o['info']['encoding'] = 'WinAnsiEncoding';
1032 }
1033 if ( $o['info']['encoding'] != 'none' )
1034 {
1035 $res.= "/BaseEncoding /" . $o['info']['encoding'] . "\n";
1036 }
1037 $res.= "/Differences \n[";
1038 $onum = -100;
1039 foreach ( $o['info']['differences'] as $num => $label )
1040 {
1041 if ($num!=$onum+1)
1042 {
1043 // we cannot make use of consecutive numbering
1044 $res.= "\n".$num." /".$label;
1045 }
1046 else
1047 {
1048 $res.= " /".$label;
1049 }
1050 $onum = $num;
1051 }
1052 $res .= "\n]\n>>\nendobj";
1053 return $res;
1054 break;
1055 }
1056 }
1057
1061 function o_procset( $id, $action, $options = '' )
1062 {
1063 if ( $action != 'new' )
1064 {
1065 $o =& $this->objects[$id];
1066 }
1067 switch ( $action )
1068 {
1069 case 'new':
1070 $this->objects[$id] = array( 't' => 'procset',
1071 'info' => array( 'PDF' => 1, 'Text' => 1 ) );
1072 $this->o_pages( $this->currentNode, 'procset', $id );
1073 $this->procsetObjectId = $id;
1074 break;
1075 case 'add':
1076 // this is to add new items to the procset list, despite the fact that this is considered
1077 // obselete, the items are required for printing to some postscript printers
1078 switch ( $options )
1079 {
1080 case 'ImageB':
1081 case 'ImageC':
1082 case 'ImageI':
1083 $o['info'][$options] = 1;
1084 break;
1085 }
1086 break;
1087 case 'out':
1088 $res = "\n" . $id . " 0 obj\n[";
1089 foreach ( $o['info'] as $label => $val )
1090 {
1091 $res .= '/'.$label.' ';
1092 }
1093 $res .= "]\nendobj";
1094 return $res;
1095 break;
1096 }
1097 }
1098
1102 function o_info( $id, $action, $options = '' )
1103 {
1104 if ( $action != 'new' )
1105 {
1106 $o =& $this->objects[$id];
1107 }
1108 switch ($action)
1109 {
1110 case 'new':
1111 $this->infoObject = $id;
1112 $date = 'D:' . date( 'Ymd' );
1113 $this->objects[$id] = array( 't' => 'info',
1114 'info' => array( 'Creator' => 'eZ Publish CMS, http://ez.no',
1115 'CreationDate' => $date ) );
1116 break;
1117 case 'Title':
1118 case 'Author':
1119 case 'Subject':
1120 case 'Keywords':
1121 case 'Creator':
1122 case 'Producer':
1123 case 'CreationDate':
1124 case 'ModDate':
1125 case 'Trapped':
1126 $o['info'][$action]=$options;
1127 break;
1128 case 'out':
1129 if ( $this->encrypted )
1130 {
1131 $this->encryptInit( $id );
1132 }
1133 $res = "\n" . $id . " 0 obj\n<<\n";
1134 foreach ( $o['info'] as $k => $v )
1135 {
1136 $res .= '/' . $k . ' (';
1137 if ( $this->encrypted )
1138 {
1139 $res .= $this->filterText( $this->ARC4( $v ) );
1140 }
1141 else
1142 {
1143 $res .= $this->filterText( $v );
1144 }
1145 $res .= ")\n";
1146 }
1147 $res .= ">>\nendobj";
1148 return $res;
1149 break;
1150 }
1151 }
1152
1156 function o_action( $id, $action, $options = '' )
1157 {
1158 if ( $action != 'new' )
1159 {
1160 $o =& $this->objects[$id];
1161 }
1162 switch ( $action )
1163 {
1164 case 'new':
1165 if ( is_array( $options ) )
1166 {
1167 $this->objects[$id] = array( 't' => 'action',
1168 'info' => $options,
1169 'type' => $options['type'] );
1170 }
1171 else
1172 {
1173 // then assume a URI action
1174 $this->objects[$id] = array( 't' => 'action',
1175 'info' => $options,
1176 'type' => 'URI' );
1177 }
1178 break;
1179 case 'out':
1180 if ( $this->encrypted )
1181 {
1182 $this->encryptInit( $id );
1183 }
1184 $res = "\n" . $id . " 0 obj\n<< /Type /Action";
1185 switch( $o['type'] )
1186 {
1187 case 'ilink':
1188 // there will be an 'label' setting, this is the name of the destination
1189 //$res.="\n/S /GoTo\n/D [".$this->destinations[(string)$o['info']['label']]." 0 R /Fit]";
1190 $res .= "\n/S /GoTo\n/D [" . $this->objects[ $this->destinations[ (string)$o['info']['label'] ] ]['info']['page'] . " 0 R /" .
1191 $this->objects[ $this->destinations[ (string)$o['info']['label'] ] ]['info']['string'] . ']';
1192 break;
1193 case 'URI':
1194 $res .= "\n/S /URI\n/URI (";
1195 if ( $this->encrypted )
1196 {
1197 $res .= $this->filterText( $this->ARC4( $o['info'] ) );
1198 }
1199 else
1200 {
1201 $res .= $this->filterText( $o['info'] );
1202 }
1203 $res .= ")";
1204 break;
1205 }
1206 $res .= "\n>>\nendobj";
1207 return $res;
1208 break;
1209 }
1210 }
1211
1216 function o_annotation( $id, $action, $options = '' )
1217 {
1218 if ( $action != 'new' )
1219 {
1220 $o =& $this->objects[$id];
1221 }
1222 switch ( $action )
1223 {
1224 case 'new':
1225 // add the annotation to the current page
1226 $pageId = $this->currentPage;
1227 $this->o_page( $pageId, 'annot', $id);
1228 // and add the action object which is going to be required
1229 switch ( $options['type'] )
1230 {
1231 case 'link':
1232 $this->objects[$id] = array( 't' => 'annotation', 'info' => $options );
1233 $this->numObj++;
1234 $this->o_action( $this->numObj, 'new', $options['url'] );
1235 $this->objects[$id]['info']['actionId'] = $this->numObj;
1236 break;
1237 case 'ilink':
1238 // this is to a named internal link
1239 $label = $options['label'];
1240 $this->objects[$id] = array( 't' => 'annotation', 'info' => $options );
1241 $this->numObj++;
1242 $this->o_action( $this->numObj, 'new', array( 'type' => 'ilink', 'label' => $label ) );
1243 $this->objects[$id]['info']['actionId'] = $this->numObj;
1244 break;
1245 }
1246 break;
1247
1248 case 'out':
1249 $res = "\n" . $id . " 0 obj\n<< /Type /Annot";
1250 switch ( $o['info']['type'] )
1251 {
1252 case 'link':
1253 case 'ilink':
1254 $res.= "\n/Subtype /Link";
1255 break;
1256 }
1257 $res.= "\n/A ".$o['info']['actionId']." 0 R";
1258 $res.= "\n/Border [0 0 0]";
1259 $res.= "\n/H /I";
1260 $res.= "\n/Rect [ ";
1261 foreach ( $o['info']['rect'] as $v )
1262 {
1263 $res.= sprintf( "%.4F ", $v );
1264 }
1265 $res.= "]";
1266 $res.= "\n>>\nendobj";
1267 return $res;
1268 break;
1269 }
1270 }
1271
1275 function o_page($id,$action,$options=''){
1276 if ($action!='new'){
1277 $o =& $this->objects[$id];
1278 }
1279 switch ($action){
1280 case 'new':
1281 $this->numPages++;
1282 $this->objects[$id]=array('t'=>'page','info'=>array('parent'=>$this->currentNode,'pageNum'=>$this->numPages));
1283 if (is_array($options)){
1284 // then this must be a page insertion, array shoudl contain 'rid','pos'=[before|after]
1285 $options['id']=$id;
1286 $this->o_pages($this->currentNode,'page',$options);
1287 } else {
1288 $this->o_pages($this->currentNode,'page',$id);
1289 }
1290 $this->currentPage=$id;
1291 //make a contents object to go with this page
1292 $this->numObj++;
1293 $this->o_contents($this->numObj,'new',$id);
1294 $this->currentContents=$this->numObj;
1295 $this->objects[$id]['info']['contents']=array();
1296 $this->objects[$id]['info']['contents'][]=$this->numObj;
1297 $match = ($this->numPages%2 ? 'odd' : 'even');
1298 foreach($this->addLooseObjects as $oId=>$target){
1299 if ($target=='all' || $match==$target){
1300 $this->objects[$id]['info']['contents'][]=$oId;
1301 }
1302 }
1303 break;
1304 case 'content':
1305 $o['info']['contents'][]=$options;
1306 break;
1307 case 'annot':
1308 // add an annotation to this page
1309 if (!isset($o['info']['annot'])){
1310 $o['info']['annot']=array();
1311 }
1312 // $options should contain the id of the annotation dictionary
1313 $o['info']['annot'][]=$options;
1314 break;
1315 case 'out':
1316 $res="\n".$id." 0 obj\n<< /Type /Page";
1317 $res.="\n/Parent ".$o['info']['parent']." 0 R";
1318 if (isset($o['info']['annot'])){
1319 $res.="\n/Annots [";
1320 foreach($o['info']['annot'] as $aId){
1321 $res.=" ".$aId." 0 R";
1322 }
1323 $res.=" ]";
1324 }
1325 $count = count($o['info']['contents']);
1326 if ($count==1){
1327 $res.="\n/Contents ".$o['info']['contents'][0]." 0 R";
1328 } else if ($count>1){
1329 $res.="\n/Contents [\n";
1330 foreach ($o['info']['contents'] as $cId){
1331 $res.=$cId." 0 R\n";
1332 }
1333 $res.="]";
1334 }
1335 $res.="\n>>\nendobj";
1336 return $res;
1337 break;
1338 }
1339 }
1340
1344 function o_contents($id,$action,$options=''){
1345 if ($action!='new'){
1346 $o =& $this->objects[$id];
1347 }
1348 switch ($action){
1349 case 'new':
1350 $this->objects[$id]=array('t'=>'contents','c'=>'','info'=>array());
1351 if (strlen($options) && intval($options)){
1352 // then this contents is the primary for a page
1353 $this->objects[$id]['onPage']=$options;
1354 } else if ($options=='raw'){
1355 // then this page contains some other type of system object
1356 $this->objects[$id]['raw']=1;
1357 }
1358 break;
1359 case 'add':
1360 // add more options to the decleration
1361 foreach ($options as $k=>$v){
1362 $o['info'][$k]=$v;
1363 }
1364 case 'out':
1365 $tmp=$o['c'];
1366 $res= "\n".$id." 0 obj\n";
1367 if (isset($this->objects[$id]['raw'])){
1368 $res.=$tmp;
1369 } else {
1370 $res.= "<<";
1371 if (function_exists('gzcompress') && $this->options['compression']){
1372 // then implement ZLIB based compression on this content stream
1373 $res.=" /Filter /FlateDecode";
1374 $tmp = gzcompress($tmp);
1375 }
1376 if ($this->encrypted){
1377 $this->encryptInit($id);
1378 $tmp = $this->ARC4($tmp);
1379 }
1380 foreach($o['info'] as $k=>$v){
1381 $res .= "\n/".$k.' '.$v;
1382 }
1383 $res.="\n/Length ".strlen($tmp)." >>\nstream\n".$tmp."\nendstream";
1384 }
1385 $res.="\nendobj\n";
1386 return $res;
1387 break;
1388 }
1389 }
1390
1394 function o_image($id,$action,$options=''){
1395 if ($action!='new'){
1396 $o =& $this->objects[$id];
1397 }
1398 switch($action){
1399 case 'new':
1400 // make the new object
1401 $this->objects[$id]=array('t'=>'image','data'=>$options['data'],'info'=>array());
1402 $this->objects[$id]['info']['Type']='/XObject';
1403 $this->objects[$id]['info']['Subtype']='/Image';
1404 $this->objects[$id]['info']['Width']=$options['iw'];
1405 $this->objects[$id]['info']['Height']=$options['ih'];
1406 if (!isset($options['type']) || $options['type']=='jpg'){
1407 if (!isset($options['channels'])){
1408 $options['channels']=3;
1409 }
1410 switch($options['channels']){
1411 case 1:
1412 $this->objects[$id]['info']['ColorSpace']='/DeviceGray';
1413 break;
1414 default:
1415 $this->objects[$id]['info']['ColorSpace']='/DeviceRGB';
1416 break;
1417 }
1418 $this->objects[$id]['info']['Filter']='/DCTDecode';
1419 $this->objects[$id]['info']['BitsPerComponent']=8;
1420 } else if ($options['type']=='png'){
1421 $this->objects[$id]['info']['Filter']='/FlateDecode';
1422 $this->objects[$id]['info']['DecodeParms']='<< /Predictor 15 /Colors '.$options['ncolor'].' /Columns '.$options['iw'].' /BitsPerComponent '.$options['bitsPerComponent'].'>>';
1423 if (strlen($options['pdata'])){
1424 $tmp = ' [ /Indexed /DeviceCMYK '.(strlen($options['pdata'])/3-1).' ';
1425 $this->numObj++;
1426 $this->o_contents($this->numObj,'new');
1427 $this->objects[$this->numObj]['c']=$options['pdata'];
1428 $tmp.=$this->numObj.' 0 R';
1429 $tmp .=' ]';
1430 $this->objects[$id]['info']['ColorSpace'] = $tmp;
1431 if (isset($options['transparency'])){
1432 switch($options['transparency']['type']){
1433 case 'indexed':
1434 $tmp=' [ '.$options['transparency']['data'].' '.$options['transparency']['data'].'] ';
1435 $this->objects[$id]['info']['Mask'] = $tmp;
1436 break;
1437 }
1438 }
1439 } else {
1440 $this->objects[$id]['info']['ColorSpace']='/'.$options['color'];
1441 }
1442 $this->objects[$id]['info']['BitsPerComponent']=$options['bitsPerComponent'];
1443 }
1444 // assign it a place in the named resource dictionary as an external object, according to
1445 // the label passed in with it.
1446 $this->o_pages($this->currentNode,'xObject',array('label'=>$options['label'],'objNum'=>$id));
1447 // also make sure that we have the right procset object for it.
1448 $this->o_procset($this->procsetObjectId,'add','ImageC');
1449 break;
1450 case 'out':
1451 $tmp=$o['data'];
1452 $res= "\n".$id." 0 obj\n<<";
1453 foreach($o['info'] as $k=>$v){
1454 $res.="\n/".$k.' '.$v;
1455 }
1456 if ($this->encrypted){
1457 $this->encryptInit($id);
1458 $tmp = $this->ARC4($tmp);
1459 }
1460 $res.="\n/Length ".strlen($tmp)." >>\nstream\n".$tmp."\nendstream\nendobj\n";
1461 return $res;
1462 break;
1463 }
1464 }
1465
1469 function o_encryption($id,$action,$options=''){
1470 if ($action!='new'){
1471 $o =& $this->objects[$id];
1472 }
1473 switch($action){
1474 case 'new':
1475 // make the new object
1476 $this->objects[$id]=array('t'=>'encryption','info'=>$options);
1477 $this->arc4_objnum=$id;
1478 // figure out the additional paramaters required
1479 $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);
1480 $len = strlen($options['owner']);
1481 if ($len>32){
1482 $owner = substr($options['owner'],0,32);
1483 } else if ($len<32){
1484 $owner = $options['owner'].substr($pad,0,32-$len);
1485 } else {
1486 $owner = $options['owner'];
1487 }
1488 $len = strlen($options['user']);
1489 if ($len>32){
1490 $user = substr($options['user'],0,32);
1491 } else if ($len<32){
1492 $user = $options['user'].substr($pad,0,32-$len);
1493 } else {
1494 $user = $options['user'];
1495 }
1496 $tmp = $this->md5_16($owner);
1497 $okey = substr($tmp,0,5);
1498 $this->ARC4_init($okey);
1499 $ovalue=$this->ARC4($user);
1500 $this->objects[$id]['info']['O']=$ovalue;
1501 // now make the u value, phew.
1502 $tmp = $this->md5_16($user.$ovalue.chr($options['p']).chr(255).chr(255).chr(255).$this->fileIdentifier);
1503 $ukey = substr($tmp,0,5);
1504
1505 $this->ARC4_init($ukey);
1506 $this->encryptionKey = $ukey;
1507 $this->encrypted=1;
1508 $uvalue=$this->ARC4($pad);
1509
1510 $this->objects[$id]['info']['U']=$uvalue;
1511 $this->encryptionKey=$ukey;
1512
1513 // initialize the arc4 array
1514 break;
1515 case 'out':
1516 $res= "\n".$id." 0 obj\n<<";
1517 $res.="\n/Filter /Standard";
1518 $res.="\n/V 1";
1519 $res.="\n/R 2";
1520 $res.="\n/O (".$this->filterText($o['info']['O']).')';
1521 $res.="\n/U (".$this->filterText($o['info']['U']).')';
1522 // and the p-value needs to be converted to account for the twos-complement approach
1523 $o['info']['p'] = (($o['info']['p']^255)+1)*-1;
1524 $res.="\n/P ".($o['info']['p']);
1525 $res.="\n>>\nendobj\n";
1526
1527 return $res;
1528 break;
1529 }
1530 }
1531
1540 function md5_16($string){
1541 $tmp = md5($string);
1542 $out='';
1543 for ($i=0;$i<=30;$i=$i+2){
1544 $out.=chr(hexdec(substr($tmp,$i,2)));
1545 }
1546 return $out;
1547 }
1548
1552 function encryptInit($id){
1553 $tmp = $this->encryptionKey;
1554 $hex = dechex($id);
1555 if (strlen($hex)<6){
1556 $hex = substr('000000',0,6-strlen($hex)).$hex;
1557 }
1558 $tmp.= chr(hexdec(substr($hex,4,2))).chr(hexdec(substr($hex,2,2))).chr(hexdec(substr($hex,0,2))).chr(0).chr(0);
1559 $key = $this->md5_16($tmp);
1560 $this->ARC4_init(substr($key,0,10));
1561 }
1562
1566 function ARC4_init($key=''){
1567 $this->arc4 = '';
1568 // setup the control array
1569 if (strlen($key)==0){
1570 return;
1571 }
1572 $k = '';
1573 while(strlen($k)<256){
1574 $k.=$key;
1575 }
1576 $k=substr($k,0,256);
1577 for ($i=0;$i<256;$i++){
1578 $this->arc4 .= chr($i);
1579 }
1580 $j=0;
1581 for ($i=0;$i<256;$i++){
1582 $t = $this->arc4[$i];
1583 $j = ($j + ord($t) + ord($k[$i]))%256;
1584 $this->arc4[$i]=$this->arc4[$j];
1585 $this->arc4[$j]=$t;
1586 }
1587 }
1588
1592 function ARC4($text){
1593 $len=strlen($text);
1594 $a=0;
1595 $b=0;
1596 $c = $this->arc4;
1597 $out='';
1598 for ($i=0;$i<$len;$i++){
1599 $a = ($a+1)%256;
1600 $t= $c[$a];
1601 $b = ($b+ord($t))%256;
1602 $c[$a]=$c[$b];
1603 $c[$b]=$t;
1604 $k = ord($c[(ord($c[$a])+ord($c[$b]))%256]);
1605 $out.=chr(ord($text[$i]) ^ $k);
1606 }
1607
1608 return $out;
1609 }
1610
1618 function addLink($url,$x0,$y0,$x1,$y1){
1619 $this->numObj++;
1620 $info = array('type'=>'link','url'=>$url,'rect'=>array($x0,$y0,$x1,$y1));
1621 $this->o_annotation($this->numObj,'new',$info);
1622 }
1623
1627 function addInternalLink($label,$x0,$y0,$x1,$y1){
1628 $this->numObj++;
1629 $info = array('type'=>'ilink','label'=>$label,'rect'=>array($x0,$y0,$x1,$y1));
1630 $this->o_annotation($this->numObj,'new',$info);
1631 }
1632
1638 function setEncryption($userPass='',$ownerPass='',$pc=array()){
1639 $p=bindec(11000000);
1640
1641 $options = array(
1642 'print'=>4
1643 ,'modify'=>8
1644 ,'copy'=>16
1645 ,'add'=>32
1646 );
1647 foreach($pc as $k=>$v){
1648 if ($v && isset($options[$k])){
1649 $p+=$options[$k];
1650 } else if (isset($options[$v])){
1651 $p+=$options[$v];
1652 }
1653 }
1654 // implement encryption on the document
1655 if ($this->arc4_objnum == 0){
1656 // then the block does not exist already, add it.
1657 $this->numObj++;
1658 if (strlen($ownerPass)==0){
1659 $ownerPass=$userPass;
1660 }
1661 $this->o_encryption($this->numObj,'new',array('user'=>$userPass,'owner'=>$ownerPass,'p'=>$p));
1662 }
1663 }
1664
1668 function checkAllHere(){
1669 }
1670
1674 function output($debug=0){
1675
1676 if ($debug){
1677 // turn compression off
1678 $this->options['compression']=0;
1679 }
1680
1681 if ($this->arc4_objnum){
1682 $this->ARC4_init($this->encryptionKey);
1683 }
1684
1685 $this->checkAllHere();
1686
1687 $xref=array();
1688 $content="%PDF-1.3\n%����\n";
1689 // $content="%PDF-1.3\n";
1690 $pos=strlen($content);
1691 foreach($this->objects as $k=>$v){
1692 $tmp='o_'.$v['t'];
1693 $cont=$this->$tmp($k,'out');
1694 $content.=$cont;
1695 $xref[]=$pos;
1696 $pos+=strlen($cont);
1697 }
1698 $content.="\nxref\n0 ".(count($xref)+1)."\n0000000000 65535 f \n";
1699 foreach($xref as $p){
1700 ++$p;
1701 $content.=substr('0000000000',0,10-strlen($p)).$p." 00000 n \n";
1702 }
1703 $content.="\ntrailer\n << /Size ".(count($xref)+1)."\n /Root 1 0 R\n /Info ".$this->infoObject." 0 R\n";
1704 // if encryption has been applied to this document then add the marker for this dictionary
1705 if ($this->arc4_objnum > 0){
1706 $content .= "/Encrypt ".$this->arc4_objnum." 0 R\n";
1707 }
1708 if (strlen($this->fileIdentifier)){
1709 $content .= "/ID[<".$this->fileIdentifier."><".$this->fileIdentifier.">]\n";
1710 }
1711 $content .= " >>\nstartxref\n".($pos+1)."\n%%EOF\n";
1712
1713 return $content;
1714 }
1715
1723 function newDocument($pageSize=array(0,0,612,792)){
1724 $this->numObj=0;
1725 $this->objects = array();
1726
1727 $this->numObj++;
1728 $this->o_catalog($this->numObj,'new');
1729
1730 $this->numObj++;
1731 $this->o_outlines($this->numObj,'new');
1732
1733 $this->numObj++;
1734 $this->o_pages($this->numObj,'new');
1735
1736 $this->o_pages($this->numObj,'mediaBox',$pageSize);
1737 $this->currentNode = 3;
1738
1739 $this->numObj++;
1740 $this->o_procset($this->numObj,'new');
1741
1742 $this->numObj++;
1743 $this->o_info($this->numObj,'new');
1744
1745 $this->numObj++;
1746 $this->o_page($this->numObj,'new');
1747
1748 // need to store the first page id as there is no way to get it to the user during
1749 // startup
1750 $this->firstPageId = $this->currentContents;
1751 }
1752
1762 function openFont( $font )
1763 {
1764 // assume that $font contains both the path and perhaps the extension to the file, split them
1765 $pos = strrpos( $font, '/' );
1766 if ( $pos === false )
1767 {
1768 $dir = './';
1769 $name = $font;
1770 }
1771 else
1772 {
1773 $dir = substr( $font, 0, $pos + 1 );
1774 $name = substr( $font, $pos + 1 );
1775 }
1776
1777 if ( substr( $name, -5 ) == '.font' )
1778 {
1779 $name = substr( $name, 0, strlen( $name ) - 5 );
1780 }
1781
1782 $this->addMessage( 'openFont: ' . $font . ' - ' . $name );
1783 if ( file_exists( $dir.'php_'.$name.'.font' ) )
1784 {
1785 $this->addMessage( 'openFont: php file exists ' . $dir . 'php_' . $name . '.font' );
1786 $tmp = file( $dir . 'php_' . $name . '.font' );
1787 $this->fonts[$font] = unserialize( $tmp[0] );
1788 if ( !isset( $this->fonts[$font]['_version_'] ) || $this->fonts[$font]['_version_'] < 1 )
1789 {
1790 // if the font file is old, then clear it out and prepare for re-creation
1791 $this->addMessage( 'openFont: clear out, make way for new version.' );
1792 unset( $this->fonts[$font] );
1793 }
1794 }
1795 if ( !isset( $this->fonts[$font] ) && file_exists( $dir.$name.'.afm' ) )
1796 {
1797 // then rebuild the php_<font>.afm file from the <font>.afm file
1798 $this->addMessage( 'openFont: build php file from ' . $dir.$name.'.afm' );
1799 $data = array();
1800 $file = file( $dir.$name.'.afm' );
1801 foreach ( $file as $rowA )
1802 {
1803 $row = trim( $rowA );
1804 $pos = strpos( $row, ' ' );
1805 if ( $pos )
1806 {
1807 // then there must be some keyword
1808 $key = substr( $row, 0, $pos );
1809 switch ( $key )
1810 {
1811 case 'FontName':
1812 case 'FullName':
1813 case 'FamilyName':
1814 case 'Weight':
1815 case 'ItalicAngle':
1816 case 'IsFixedPitch':
1817 case 'CharacterSet':
1818 case 'UnderlinePosition':
1819 case 'UnderlineThickness':
1820 case 'Version':
1821 case 'EncodingScheme':
1822 case 'CapHeight':
1823 case 'XHeight':
1824 case 'Ascender':
1825 case 'Descender':
1826 case 'StdHW':
1827 case 'StdVW':
1828 case 'StartCharMetrics':
1829 $data[$key] = trim( substr( $row, $pos ) );
1830 break;
1831 case 'FontBBox':
1832 $data[$key] = explode(' ', trim( substr( $row, $pos ) ) );
1833 break;
1834 case 'C':
1835 //C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ;
1836 $bits = explode( ';', trim( $row ) );
1837 $dtmp = array();
1838 foreach ( $bits as $bit )
1839 {
1840 $bits2 = explode( ' ', trim( $bit ) );
1841 if ( strlen( $bits2[0] ) )
1842 {
1843 if ( count( $bits2 ) > 2 )
1844 {
1845 $dtmp[$bits2[0]] = array();
1846 for ( $i = 1; $i < count( $bits2 ); $i++ )
1847 {
1848 $dtmp[$bits2[0]][] = $bits2[$i];
1849 }
1850 }
1851 else if ( count( $bits2 ) == 2 )
1852 {
1853 $dtmp[$bits2[0]] = $bits2[1];
1854 }
1855 }
1856 }
1857 if ( $dtmp['C'] >= 0 )
1858 {
1859 $data['C'][$dtmp['C']] = $dtmp;
1860 $data['C'][$dtmp['N']] = $dtmp;
1861 }
1862 else
1863 {
1864 $data['C'][$dtmp['N']] = $dtmp;
1865 }
1866 break;
1867
1868 case 'KPX':
1869 //KPX Adieresis yacute -40
1870 $bits = explode( ' ', trim( $row ) );
1871 $data['KPX'][$bits[1]][$bits[2]] = $bits[3];
1872 break;
1873 }
1874 }
1875 }
1876 $data['_version_'] = 1;
1877 $this->fonts[$font] = $data;
1878 $fp = fopen( $dir.'php_'.$name.'.font', 'w' );
1879 fwrite( $fp, serialize( $data ) );
1880 fclose( $fp );
1881 }
1882 else if ( !isset( $this->fonts[$font] ) )
1883 {
1884 $this->addMessage( 'openFont: no font file found' );
1885 //echo 'Font not Found '.$font;
1886 }
1887 }
1888
1897 function selectFont( $fontName, $encoding = '', $set = 1 )
1898 {
1899 if ( !isset( $this->fonts[$fontName] ) )
1900 {
1901 // load the file
1902 $this->openFont( $fontName );
1903 if ( isset( $this->fonts[$fontName] ) )
1904 {
1905 $this->numObj++;
1906 $this->numFonts++;
1907 $pos = strrpos( $fontName, '/' );
1908 $name = substr( $fontName, $pos + 1 );
1909 if ( substr( $name, -5 ) == '.font' )
1910 {
1911 $name = substr( $name, 0, strlen( $name ) - 5 );
1912 }
1913 if ( !isset( $this->fontFamilies[$name] ) )
1914 {
1915 $this->setFontFamily( $name, array( 'b' => $name . '-Bold',
1916 'i' => $name . '-Italic',
1917 'bi' => $name . '-BoldItalic',
1918 'ib' => $name . '-BoldItalic' ) );
1919 }
1920 $options = array( 'name' => $name );
1921 if ( is_array( $encoding ) )
1922 {
1923 // then encoding and differences might be set
1924 if ( isset( $encoding['encoding'] ) )
1925 {
1926 $options['encoding'] = $encoding['encoding'];
1927 }
1928 if ( isset( $encoding['differences'] ) )
1929 {
1930 $options['differences'] = $encoding['differences'];
1931 }
1932 }
1933 else if ( strlen( $encoding ) )
1934 {
1935 // then perhaps only the encoding has been set
1936 $options['encoding'] = $encoding;
1937 }
1938 $fontObj = $this->numObj;
1939 $this->o_font( $this->numObj, 'new', $options );
1940 $this->fonts[$fontName]['fontNum'] = $this->numFonts;
1941 // if this is a '.afm' font, and there is a '.pfa' file to go with it ( as there
1942 // should be for all non-basic fonts), then load it into an object and put the
1943 // references into the font object
1944 $basefile = substr( $fontName, 0, strlen( $fontName ) );
1945 if ( file_exists( $basefile.'.pfb' ) )
1946 {
1947 $fbtype = 'pfb';
1948 }
1949 else if ( file_exists( $basefile.'.ttf' ) )
1950 {
1951 $fbtype = 'ttf';
1952 }
1953 else
1954 {
1955 $fbtype = '';
1956 }
1957 $fbfile = $basefile.'.'.$fbtype;
1958
1959 $this->addMessage( 'selectFont: checking for - ' . $fbfile );
1960 if ( strlen( $fbtype ) )
1961 {
1962 $adobeFontName = $this->fonts[$fontName]['FontName'];
1963 $this->addMessage( 'selectFont: adding font file - ' . $fbfile . ' - ' . $adobeFontName );
1964 // find the array of fond widths, and put that into an object.
1965 $firstChar = -1;
1966 $lastChar = 0;
1967 $widths = array();
1968 foreach ( $this->fonts[$fontName]['C'] as $num => $d )
1969 {
1970 if ( intval($num) > 0 || $num == '0' )
1971 {
1972 if ( $lastChar > 0 && $num > $lastChar + 1 )
1973 {
1974 for ( $i = $lastChar + 1; $i < $num; $i++ )
1975 {
1976 $widths[] = 0;
1977 }
1978 }
1979 $widths[] = $d['WX'];
1980 if ( $firstChar == -1 )
1981 {
1982 $firstChar = $num;
1983 }
1984 $lastChar = $num;
1985 }
1986 }
1987 // also need to adjust the widths for the differences array
1988 if ( isset( $options['differences'] ) )
1989 {
1990 foreach( $options['differences'] as $charNum => $charName )
1991 {
1992 if ( $charNum > $lastChar )
1993 {
1994 for( $i = $lastChar + 1; $i <= $charNum; $i++ )
1995 {
1996 $widths[] = 0;
1997 }
1998 $lastChar = $charNum;
1999 }
2000 if ( isset( $this->fonts[$fontName]['C'][$charName] ) )
2001 {
2002 $widths[$charNum-$firstChar] = $this->fonts[$fontName]['C'][$charName]['WX'];
2003 }
2004 }
2005 }
2006 $this->addMessage( 'selectFont: FirstChar=' . $firstChar );
2007 $this->addMessage( 'selectFont: LastChar=' . $lastChar );
2008 $this->numObj++;
2009 $this->o_contents( $this->numObj, 'new', 'raw' );
2010 $this->objects[$this->numObj]['c'] .= '[';
2011 foreach ( $widths as $width )
2012 {
2013 $this->objects[$this->numObj]['c'] .= ' ' . $width;
2014 }
2015 $this->objects[$this->numObj]['c'] .= ' ]';
2016 $widthid = $this->numObj;
2017
2018 // load the pfb file, and put that into an object too.
2019 // note that pdf supports only binary format type 1 font files, though there is a
2020 // simple utility to convert them from pfa to pfb.
2021 $fp = fopen( $fbfile, 'rb' );
2022 $data = fread( $fp, filesize( $fbfile ) );
2023 fclose( $fp );
2024
2025 // create the font descriptor
2026 $this->numObj++;
2027 $fontDescriptorId = $this->numObj;
2028 $this->numObj++;
2029 $pfbid = $this->numObj;
2030 // determine flags (more than a little flakey, hopefully will not matter much)
2031 $flags=0;
2032 if ( $this->fonts[$fontName]['ItalicAngle'] != 0 )
2033 {
2034 $flags += pow( 2, 6 );
2035 }
2036 if ( $this->fonts[$fontName]['IsFixedPitch'] == 'true' )
2037 {
2038 $flags += 1;
2039 }
2040 $flags += pow( 2, 5 ); // assume non-sybolic
2041
2042 $list = array( 'Ascent' => 'Ascender',
2043 'CapHeight' => 'CapHeight',
2044 'Descent' => 'Descender',
2045 'FontBBox' => 'FontBBox',
2046 'ItalicAngle' => 'ItalicAngle' );
2047 $fdopt = array( 'Flags' => $flags,
2048 'FontName' => $adobeFontName,
2049 'StemV' => 100 // don't know what the value for this should be!
2050 );
2051 foreach ( $list as $k => $v )
2052 {
2053 if ( isset( $this->fonts[$fontName][$v] ) )
2054 {
2055 $fdopt[$k] = $this->fonts[$fontName][$v];
2056 }
2057 }
2058
2059 if ( $fbtype == 'pfb' )
2060 {
2061 $fdopt['FontFile'] = $pfbid;
2062 }
2063 else if ( $fbtype == 'ttf' )
2064 {
2065 $fdopt['FontFile2'] = $pfbid;
2066 }
2067 $this->o_fontDescriptor( $fontDescriptorId, 'new', $fdopt );
2068
2069 // embed the font program
2070 $this->o_contents( $this->numObj, 'new' );
2071 $this->objects[$pfbid]['c'] .= $data;
2072 // determine the cruicial lengths within this file
2073 if ($fbtype=='pfb')
2074 {
2075 $l1 = strpos($data,'eexec') + 6;
2076 $l2 = strpos($data,'00000000') - $l1;
2077 $l3 = strlen($data) - $l2 - $l1;
2078 $this->o_contents( $this->numObj, 'add', array( 'Length1' => $l1,
2079 'Length2' => $l2,
2080 'Length3' => $l3 ) );
2081 }
2082 else if ( $fbtype == 'ttf' )
2083 {
2084 $l1 = strlen($data);
2085 $this->o_contents( $this->numObj, 'add', array( 'Length1' => $l1 ) );
2086 }
2087
2088 // tell the font object about all this new stuff
2089 $tmp = array( 'BaseFont' => $adobeFontName,
2090 'Widths' => $widthid,
2091 'FirstChar' => $firstChar,
2092 'LastChar' => $lastChar,
2093 'FontDescriptor' => $fontDescriptorId );
2094 if ( $fbtype == 'ttf' )
2095 {
2096 $tmp['SubType'] = 'TrueType';
2097 }
2098 $this->addMessage( 'adding extra info to font.('.$fontObj.')' );
2099 foreach ( $tmp as $fk => $fv )
2100 {
2101 $this->addMessage( $fk." : ".$fv );
2102 }
2103 $this->o_font( $fontObj, 'add', $tmp );
2104
2105 }
2106 else
2107 {
2108 $this->addMessage( 'selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts' );
2109 }
2110
2111
2112 // also set the differences here, note that this means that these will take effect only the
2113 //first time that a font is selected, else they are ignored
2114 if ( isset( $options['differences'] ) )
2115 {
2116 $this->fonts[$fontName]['differences'] = $options['differences'];
2117 }
2118 }
2119 }
2120 if ( $set && isset( $this->fonts[$fontName] ) )
2121 {
2122 // so if for some reason the font was not set in the last one then it will not be selected
2123 $this->currentBaseFont = $fontName;
2124 // the next line means that if a new font is selected, then the current text state will be
2125 // applied to it as well.
2126 $this->setCurrentFont();
2127 }
2128 return $this->currentFontNum;
2129 }
2130
2136 function currentFont()
2137 {
2138 return $this->currentBaseFont;
2139 }
2140
2153 function setCurrentFont()
2154 {
2155 if ( strlen( $this->currentBaseFont ) == 0 )
2156 {
2157 // then assume an initial font
2158 $this->selectFont( 'lib/ezpdf/classes/fonts/Helvetica' );
2159 }
2160 $cf = substr( $this->currentBaseFont, strrpos( $this->currentBaseFont, '/' ) + 1 );
2161 if ( strlen( $this->currentTextState )
2162 && isset( $this->fontFamilies[$cf] )
2163 && isset( $this->fontFamilies[$cf][$this->currentTextState] ) )
2164 {
2165 // then we are in some state or another
2166 // and this font has a family, and the current setting exists within it
2167 // select the font, then return it
2168 $nf = substr( $this->currentBaseFont, 0, strrpos( $this->currentBaseFont, '/' ) + 1 ) .
2169 $this->fontFamilies[$cf][$this->currentTextState];
2170 $this->selectFont( $nf, '', 0 );
2171 $this->currentFont = $nf;
2172 $this->currentFontNum = $this->fonts[$nf]['fontNum'];
2173 }
2174 else
2175 {
2176 // the this font must not have the right family member for the current state
2177 // simply assume the base font
2178 $this->currentFont = $this->currentBaseFont;
2179 $this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
2180 }
2181 }
2182
2187 function getFirstPageId()
2188 {
2189 return $this->firstPageId;
2190 }
2191
2197 function addContent( $content )
2198 {
2199 $this->objects[$this->currentContents]['c'] .= $content;
2200 }
2201
2205 function setColorRGB( $r, $g, $b, $force = 0 )
2206 {
2207 $this->setColor( eZMath::rgbToCMYK2( $r, $g, $b ), $force );
2208 }
2209
2215 function setColor( $cmykArray, $force = 0 )
2216 {
2217 if ( $force || !$this->compareCMYK( $cmykArray, $this->currentColour ) )
2218 {
2219 $this->objects[$this->currentContents]['c'] .= "\n" .
2220 sprintf( '%.3F', $cmykArray['c'] ) . ' ' . sprintf( '%.3F', $cmykArray['m'] ) . ' ' .
2221 sprintf( '%.3F', $cmykArray['y'] ) . ' ' . sprintf( '%.3F', $cmykArray['k'] ) . ' k';
2222 $this->currentColour = $cmykArray;
2223 }
2224 }
2225
2229 function setStrokeColorRGB( $r, $g, $b, $force = 0 )
2230 {
2231 $this->setStrokeColor( eZMath::rgbToCMYK2( $r, $g, $b ), $force );
2232 }
2233
2240 function setStrokeColor( $cmykArray, $force = 0 )
2241 {
2242 if ( $force || !$this->compareCMYK( $cmykArray, $this->currentStrokeColour ) )
2243 {
2244 $this->objects[$this->currentContents]['c'] .= "\n" .
2245 sprintf( '%.3F', $cmykArray['c'] ) . ' ' . sprintf( '%.3F', $cmykArray['m'] ) . ' ' .
2246 sprintf( '%.3F', $cmykArray['y'] ) . ' ' . sprintf( '%.3F', $cmykArray['k'] ) . ' K';
2247 $this->currentStrokeColour = $cmykArray;
2248 }
2249 }
2250
2254 function line( $x1, $y1, $x2, $y2 )
2255 {
2256 $this->objects[$this->currentContents]['c'] .= "\n".sprintf('%.3F',$x1).' '.sprintf('%.3F',$y1).' m '.sprintf('%.3F',$x2).' '.sprintf('%.3F',$y2).' l S';
2257 }
2258
2262 function curve( $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3 )
2263 {
2264 // in the current line style, draw a bezier curve from (x0,y0) to (x3,y3) using the other two points
2265 // as the control points for the curve.
2266 $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3F',$x0).' '.sprintf('%.3F',$y0).' m '.sprintf('%.3F',$x1).' '.sprintf('%.3F',$y1);
2267 $this->objects[$this->currentContents]['c'].= ' '.sprintf('%.3F',$x2).' '.sprintf('%.3F',$y2).' '.sprintf('%.3F',$x3).' '.sprintf('%.3F',$y3).' c S';
2268 }
2269
2273 function partEllipse( $x0, $y0, $astart, $afinish, $r1, $r2 = 0, $angle = 0, $nSeg = 8 )
2274 {
2275 $this->ellipse( $x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, 0 );
2276 }
2277
2281 function filledEllipse( $x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360 )
2282 {
2283 return $this->ellipse( $x0, $y0, $r1, $r2 = 0, $angle, $nSeg, $astart, $afinish, 1, 1 );
2284 }
2285
2296 function ellipse( $x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360, $close = 1, $fill = 0 )
2297 {
2298 if ( $r1 == 0 )
2299 {
2300 return;
2301 }
2302 if ( $r2 == 0 )
2303 {
2304 $r2 = $r1;
2305 }
2306 if ( $nSeg < 2 )
2307 {
2308 $nSeg = 2;
2309 }
2310
2311 $astart = deg2rad( (float)$astart );
2312 $afinish = deg2rad( (float)$afinish );
2313 $totalAngle = $afinish - $astart;
2314
2315 $dt = $totalAngle / $nSeg;
2316 $dtm = $dt / 3;
2317
2318 if ( $angle != 0 )
2319 {
2320 $a = -1*deg2rad((float)$angle);
2321 $tmp = "\n q ";
2322 $tmp .= sprintf('%.3F',cos($a)).' '.sprintf('%.3F',(-1.0*sin($a))).' '.sprintf('%.3F',sin($a)).' '.sprintf('%.3F',cos($a)).' ';
2323 $tmp .= sprintf('%.3F',$x0).' '.sprintf('%.3F',$y0).' cm';
2324 $this->objects[$this->currentContents]['c'].= $tmp;
2325 $x0=0;
2326 $y0=0;
2327 }
2328
2329 $t1 = $astart;
2330 $a0 = $x0 + $r1*cos($t1);
2331 $b0 = $y0 + $r2*sin($t1);
2332 $c0 = -$r1 * sin($t1);
2333 $d0 = $r2 * cos($t1);
2334
2335 $this->objects[$this->currentContents]['c'] .= "\n".sprintf('%.3F',$a0).' '.sprintf('%.3F',$b0).' m ';
2336 for ($i=1;$i<=$nSeg;$i++)
2337 {
2338 // draw this bit of the total curve
2339 $t1 = $i*$dt + $astart;
2340 $a1 = $x0 + $r1*cos($t1);
2341 $b1 = $y0 + $r2*sin($t1);
2342 $c1 = -$r1 * sin($t1);
2343 $d1 = $r2 * cos($t1);
2344 $this->objects[$this->currentContents]['c'].= "\n".sprintf('%.3F',($a0+$c0*$dtm)).' '.sprintf('%.3F',($b0+$d0*$dtm));
2345 $this->objects[$this->currentContents]['c'].= ' '.sprintf('%.3F',($a1-$c1*$dtm)).' '.sprintf('%.3F',($b1-$d1*$dtm)).' '.sprintf('%.3F',$a1).' '.sprintf('%.3F',$b1).' c';
2346 $a0 = $a1;
2347 $b0 = $b1;
2348 $c0 = $c1;
2349 $d0 = $d1;
2350 }
2351 if ( $fill )
2352 {
2353 $this->objects[$this->currentContents]['c'].= ' f';
2354 }
2355 else
2356 {
2357 if ($close)
2358 {
2359 $this->objects[$this->currentContents]['c'].= ' s'; // small 's' signifies closing the path as well
2360 }
2361 else
2362 {
2363 $this->objects[$this->currentContents]['c'].= ' S';
2364 }
2365 }
2366 if ( $angle != 0 )
2367 {
2368 $this->objects[$this->currentContents]['c'].= ' Q';
2369 }
2370 }
2371
2385 function setLineStyle( $width = 1, $cap = '', $join = '', $dash = '', $phase = 0 )
2386 {
2387
2388 // this is quite inefficient in that it sets all the parameters whenever 1 is changed, but will fix another day
2389 $string = '';
2390 if ( $width > 0 )
2391 {
2392 $string.= $width.' w';
2393 }
2394 $ca = array( 'butt' => 0, 'round' => 1, 'square' => 2 );
2395 if ( isset( $ca[$cap] ) )
2396 {
2397 $string.= ' '.$ca[$cap].' J';
2398 }
2399 $ja = array('miter'=>0,'round'=>1,'bevel'=>2);
2400 if ( isset( $ja[$join] ) )
2401 {
2402 $string.= ' '.$ja[$join].' j';
2403 }
2404 if ( is_array( $dash ) )
2405 {
2406 $string.= ' [';
2407 foreach ( $dash as $len )
2408 {
2409 $string.=' '.$len;
2410 }
2411 $string.= ' ] '.$phase.' d';
2412 }
2413 $this->currentLineStyle = $string;
2414 $this->objects[$this->currentContents]['c'].= "\n".$string;
2415 }
2416
2420 function polygon( $p, $np, $f = 0 )
2421 {
2422 $this->objects[$this->currentContents]['c'].= "\n";
2423 $this->objects[$this->currentContents]['c'].= sprintf('%.3F',$p[0]).' '.sprintf('%.3F',$p[1]).' m ';
2424 for ( $i = 2; $i < $np*2; $i = $i + 2 )
2425 {
2426 $this->objects[$this->currentContents]['c'].= sprintf('%.3F',$p[$i]).' '.sprintf('%.3F',$p[$i+1]).' l ';
2427 }
2428 if ( $f == 1 )
2429 {
2430 $this->objects[$this->currentContents]['c'].= ' f';
2431 }
2432 else
2433 {
2434 $this->objects[$this->currentContents]['c'].= ' S';
2435 }
2436 }
2437
2450 function shadedRectangle( $x1, $y1, $width, $height, $options )
2451 {
2452 $this->numObj++;
2453 $shadingLabel = $this->o_shading( $this->numObj, 'new', $options );
2454
2455 $this->saveState();
2456
2457 $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3F',$x1).' '.sprintf('%.3F',$y1).' '.sprintf('%.3F',$width).' '.sprintf('%.3F',$height).' re';
2458
2459 $this->objects[$this->currentContents]['c'].="\nW n";
2460
2461 $this->objects[$this->currentContents]['c'].="\n/".$shadingLabel.' sh';
2462
2463 $this->restoreState();
2464 }
2465
2470 function filledRectangle( $x1, $y1, $width, $height )
2471 {
2472 $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3F',$x1).' '.sprintf('%.3F',$y1).' '.sprintf('%.3F',$width).' '.sprintf('%.3F',$height).' re f';
2473 }
2474
2479 function rectangle( $x1, $y1, $width, $height )
2480 {
2481 $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3F',$x1).' '.sprintf('%.3F',$y1).' '.sprintf('%.3F',$width).' '.sprintf('%.3F',$height).' re S';
2482 }
2483
2488 function newPage( $insert = 0, $id = 0, $pos = 'after' )
2489 {
2490 // if there is a state saved, then go up the stack closing them
2491 // then on the new page, re-open them with the right setings
2492
2493 if ( $this->nStateStack )
2494 {
2495 for ( $i = $this->nStateStack; $i >= 1; $i-- )
2496 {
2497 $this->restoreState( $i );
2498 }
2499 }
2500
2501 $this->numObj++;
2502 if ( $insert )
2503 {
2504 // the id from the ezPdf class is the od of the contents of the page, not the page object itself
2505 // query that object to find the parent
2506 $rid = $this->objects[$id]['onPage'];
2507 $opt = array( 'rid' => $rid, 'pos' => $pos );
2508 $this->o_page( $this->numObj, 'new', $opt);
2509 }
2510 else
2511 {
2512 $this->o_page( $this->numObj, 'new' );
2513 }
2514 // if there is a stack saved, then put that onto the page
2515 if ( $this->nStateStack )
2516 {
2517 for ( $i = 1; $i <= $this->nStateStack; $i++)
2518 {
2519 $this->saveState($i);
2520 }
2521 }
2522 // and if there has been a stroke or fill colour set, then transfer them
2523 if ( $this->currentColour['c'] >= 0 )
2524 {
2525 $this->setColor( $this->currentColour, 1 );
2526 }
2527 if ( $this->currentStrokeColour['c'] >= 0 )
2528 {
2529 $this->setStrokeColor( $this->currentStrokeColour, 1 );
2530 }
2531
2532 // if there is a line style set, then put this in too
2533 if ( strlen( $this->currentLineStyle ) )
2534 {
2535 $this->objects[$this->currentContents]['c'].= "\n".$this->currentLineStyle;
2536 }
2537
2538 // the call to the o_page object set currentContents to the present page, so this can be returned as the page id
2539 return $this->currentContents;
2540 }
2541
2546 function stream( $options = '' )
2547 {
2548 // setting the options allows the adjustment of the headers
2549 // values at the moment are:
2550 // 'Content-Disposition'=>'filename' - sets the filename, though not too sure how well this will
2551 // work as in my trial the browser seems to use the filename of the php file with .pdf on the end
2552 // 'Accept-Ranges'=>1 or 0 - if this is not set to 1, then this header is not included, off by default
2553 // this header seems to have caused some problems despite tha fact that it is supposed to solve
2554 // them, so I am leaving it off by default.
2555 // 'compress'=> 1 or 0 - apply content stream compression, this is on (1) by default
2556 if ( !is_array( $options ) )
2557 {
2558 $options = array();
2559 }
2560 if ( isset( $options['compress'] ) && $options['compress'] == 0 )
2561 {
2562 $tmp = $this->output( 1 );
2563 }
2564 else
2565 {
2566 $tmp = $this->output();
2567 }
2568 $tmp = ltrim( $tmp );
2569
2570 ob_clean();
2571
2572 header( 'Pragma: ' );
2573 header( 'Cache-Control: ' );
2574 /* Set cache time out to 10 seconds, this should be good enough to work around an IE bug */
2575 header( "Expires: ". gmdate( 'D, d M Y H:i:s', time() + 10 ) . ' GMT' );
2576 header( 'X-Powered-By: eZ Publish' );
2577
2578 header( 'Content-Length: '.strlen( $tmp ) );
2579 header( 'Content-Type: application/pdf' );
2580 header( 'Content-Transfer-Encoding: binary' );
2581 header( 'Accept-Ranges: bytes' );
2582
2583 ob_end_clean();
2584
2585 echo $tmp;
2586
2587 eZExecution::cleanExit();
2588 }
2589
2593 function getFontHeight( $size = false )
2594 {
2595 if ( !$this->numFonts )
2596 {
2597 $this->selectFont( './fonts/Helvetica' );
2598 }
2599 // for the current font, and the given size, what is the height of the font in user units
2600 $h = $this->fonts[$this->currentFont]['FontBBox'][3]-$this->fonts[$this->currentFont]['FontBBox'][1];
2601 if ( $size === false )
2602 {
2603 $size = $this->fontSize();
2604 }
2605 return $this->ez['lineSpace'] * $size * $h / 1000;
2606 }
2607
2613 function getFontDecender( $size = false )
2614 {
2615 // note that this will most likely return a negative value
2616 if ( !$this->numFonts )
2617 {
2618 $this->selectFont( './fonts/Helvetica' );
2619 }
2620 $h = $this->fonts[$this->currentFont]['FontBBox'][1];
2621 if ( $size === false )
2622 {
2623 $size = $this->fontSize();
2624 }
2625 return $size*$h/1000;
2626 }
2627
2634 function filterText( $text )
2635 {
2636 $text = str_replace('\\','\\\\',$text);
2637 $text = str_replace('(','\‍(',$text);
2638 $text = str_replace(')','\‍)',$text);
2639 $text = str_replace('&lt;','<',$text);
2640 $text = str_replace('&gt;','>',$text);
2641 $text = str_replace('&#039;','\'',$text);
2642 $text = str_replace('&quot;','"',$text);
2643 $text = str_replace('&amp;','&',$text);
2644
2645 return $text;
2646 }
2647
2654 function PRVTgetTextPosition( $x, $y, $angle, $size, $wa, $text )
2655 {
2656 $tmp = false;
2657 // given this information return an array containing x and y for the end position as elements 0 and 1
2658
2659 $w = $this->getTextWidth($size,$text);
2660 // need to adjust for the number of spaces in this text
2661 $words = explode(' ',$text);
2662 $nspaces=count($words)-1;
2663 $w += $wa*$nspaces;
2664 $a = deg2rad((float)$angle);
2665 if ( $tmp )
2666 {
2667 return array($this->xOffset(),-sin($a)*$w+$y);
2668 }
2669 return array(cos($a)*$w+$x,-sin($a)*$w+$y);
2670 }
2671
2677 function PRVTcheckTextDirective( &$text, $i, &$f, $final = 0 )
2678 {
2679 $x = 0;
2680 $y = 0;
2681 return $this->PRVTcheckTextDirective1( $text, $i, $f, $final, $x, $y );
2682 }
2683
2693 function PRVTcheckTextDirective1( &$text, $i, &$f, $final, &$x, &$y, $size = 0, $angle = 0, $wordSpaceAdjust = 0 )
2694 {
2695 $newTextState = $this->currentTextState;
2696 $noClose = 0;
2697 $directive = 0;
2698 $j = $i;
2699 if ( $text[$j] == '<' )
2700 {
2701 $j++;
2702 switch ( $text[$j] )
2703 {
2704 case '/':
2705 $j++;
2706 if (strlen($text) <= $j)
2707 {
2708 return $directive;
2709 }
2710 switch ( $text[$j] )
2711 {
2712 case 'b':
2713 case 'i':
2714 ++$j;
2715 if ( $text[$j] == '>' )
2716 {
2717 if ( $final === true )
2718 $this->currentTextState = substr( $this->currentTextState, 0, $p).substr( $this->currentTextState, $p+1);
2719 $directive = $j-$i+1;
2720 }
2721 break;
2722
2723 case 'c':
2724 {
2725 // this this might be a callback function
2726 $j++;
2727 $k = strpos( $text, '>', $j );
2728 if ( $k !== false && $text[$j] == ':' )
2729 {
2730 // then this will be treated as a callback directive
2731 $directive = $k-$i+1;
2732 $f = 0;
2733 // split the remainder on colons to get the function name and the paramater
2734 $tmp = substr( $text, $j+1, $k-$j-1 );
2735 $b1 = strpos( $tmp, ':' );
2736 if ( $b1 !== false )
2737 {
2738 $func = substr( $tmp, 0, $b1 );
2739 $parm = substr( $tmp, $b1+1 );
2740 }
2741 else
2742 {
2743 $func = $tmp;
2744 $parm = '';
2745 }
2746 if ( !isset( $func ) || !strlen( trim( $func ) ) )
2747 {
2748 $directive = 0;
2749 }
2750 else
2751 {
2752 // only call the function if this is the final call
2753 if ( $final === 1 )
2754 {
2755 // need to assess the text position, calculate the text width to this point
2756 // can use getTextWidth to find the text width I think
2757 if ( $x < $this->leftMargin() )
2758 {
2759 $x = $this->leftMargin();
2760 }
2761 $startInfo = $this->callback[$this->nCallback];
2762
2763 if ( $x < $this->xOffset() )
2764 {
2765 $x = $this->xOffset();
2766 }
2767 $tmp = $this->PRVTgetTextPosition( $x, $y, $angle, $size, $wordSpaceAdjust, substr( $text, $startInfo['i'], $i-$startInfo['i'] ) );
2768 $info = array( 'x' => $tmp[0],'y' => $tmp[1], 'angle' => $angle, 'status' => 'end', 'p' => $parm, 'nCallback' => $this->nCallback );
2769
2770 $x = $tmp[0];
2771 $y = $tmp[1];
2772 $ret = $this->$func( $info );
2773 if ( is_array( $ret ) )
2774 {
2775 // then the return from the callback function could set the position, to start with, later will do font colour, and font
2776 foreach ( $ret as $rk => $rv )
2777 {
2778 switch ( $rk )
2779 {
2780 case 'x':
2781 case 'y':
2782 $$rk = $rv;
2783 break;
2784 }
2785 }
2786 }
2787 // also remove from to the stack
2788 // for simplicity, just take from the end, fix this another day
2789 $this->nCallback--;
2790 if ( $this->nCallback < 0 )
2791 {
2792 $this->nCallBack = 0;
2793 }
2794 }
2795 }
2796 }
2797 } break;
2798 }
2799 break;
2800
2801 case 'b':
2802 case 'i':
2803 ++$j;
2804
2805 if ( $text[$j] == '>' )
2806 {
2807 if ( $final === true )
2808 $this->currentTextState .= $text[$j-1];
2809 $directive = $j-$i+1;
2810 }
2811 break;
2812
2813 case 'C':
2814 $noClose = 1;
2815 case 'c':
2816 // this this might be a callback function
2817 $j++;
2818 $k = strpos( $text, '>', $j );
2819 if ( $k !== false && $text[$j] == ':' )
2820 {
2821 // then this will be treated as a callback directive
2822 $directive = $k-$i+1;
2823 $f=0;
2824 // split the remainder on colons to get the function name and the paramater
2825 $tmp = substr( $text, $j+1, $k-$j-1 );
2826 $b1 = strpos( $tmp, ':' );
2827 if ( $b1 !== false )
2828 {
2829 $func = substr( $tmp, 0, $b1 );
2830 $parm = substr( $tmp, $b1+1 );
2831 }
2832 else
2833 {
2834 $func = $tmp;
2835 $parm = '';
2836 }
2837 if ( !isset( $func ) || !strlen( trim( $func ) ) )
2838 {
2839 $directive=0;
2840 }
2841 else
2842 {
2843 // only call the function if this is the final call, ie, the one actually doing printing, not measurement
2844 if ($final === 1)
2845 {
2846 // need to assess the text position, calculate the text width to this point
2847 // can use getTextWidth to find the text width I think
2848 // also add the text height and decender
2849 $tmp = $this->PRVTgetTextPosition($x,$y,$angle,$size,$wordSpaceAdjust,substr($text,0,$i));
2850 if ( $tmp[0] < $this->leftMargin() )
2851 {
2852 $tmp[0] = $this->leftMargin();
2853 }
2854 if ( $tmp[0] < $this->xOffset() )
2855 {
2856 $tmp[0] = $this->xOffset();
2857 }
2858 $info = array( 'x' => $tmp[0],
2859 'y' => $tmp[1],
2860 'angle' => $angle,
2861 'status' => 'start',
2862 'p' => $parm,
2863 'f' => $func,
2864 'height' => $this->getFontHeight( $size ),
2865 'decender' => $this->getFontDecender( $size ),
2866 'i' => $i );
2867
2868 $x = $tmp[0];
2869 $y = $tmp[1];
2870 if (!isset($noClose) || !$noClose)
2871 {
2872 // only add to the stack if this is a small 'c', therefore is a start-stop pair
2873 $this->nCallback++;
2874 $info['nCallback']=$this->nCallback;
2875 $this->callback[$this->nCallback]=$info;
2876 }
2877 $ret = $this->$func( $info );
2878 if ( is_array( $ret ) )
2879 {
2880 // then the return from the callback function could set the position, to start with, later will do font colour, and font
2881 foreach ( $ret as $rk => $rv )
2882 {
2883 switch ( $rk )
2884 {
2885 case 'x':
2886 case 'y':
2887 $$rk=$rv;
2888 break;
2889 }
2890 }
2891 }
2892 }
2893 }
2894 }
2895 break;
2896 }
2897 }
2898 return array( 'directive' => $directive,
2899 'y' => (float)$y,
2900 'noClose' => $noClose,
2901 'run_final' => ( $f == 0 ) );
2902 }
2903
2909 function addText( $x, $y, $size, $text, $angle = 0, $wordSpaceAdjust = 0 )
2910 {
2911 $returnArray = array( 'height' => -1 );
2912
2913 if (!$this->numFonts)
2914 {
2915 $this->selectFont('./fonts/Helvetica');
2916 }
2917
2918 // if there are any open callbacks, then they should be called, to show the start of the line
2919 if ($this->nCallback>0)
2920 {
2921 for ( $i = $this->nCallback; $i > 0; $i-- )
2922 {
2923 // call each function
2924 $info = array( 'x' => $x, 'y' => $y,
2925 'angle' => $angle,
2926 'status' => 'sol',
2927 'p' => $this->callback[$i]['p'],
2928 'nCallback' => $this->callback[$i]['nCallback'],
2929 'height' => $this->callback[$i]['height'],
2930 'decender' => $this->callback[$i]['decender'] );
2931 $func = $this->callback[$i]['f'];
2932 $this->$func( $info );
2933 }
2934 }
2935 if ( $angle == 0 )
2936 {
2937 $this->objects[$this->currentContents]['c'] .= "\n".'BT '.sprintf('%.3F',$x).' '.sprintf('%.3F',$y).' Td';
2938 }
2939 else
2940 {
2941 $a = deg2rad((float)$angle);
2942 $tmp = "\n".'BT ';
2943 $tmp .= sprintf('%.3F',cos($a)).' '.sprintf('%.3F',(-1.0*sin($a))).' '.sprintf('%.3F',sin($a)).' '.sprintf('%.3F',cos($a)).' ';
2944 $tmp .= sprintf('%.3F',$x).' '.sprintf('%.3F',$y).' Tm';
2945 $this->objects[$this->currentContents]['c'] .= $tmp;
2946 }
2947 if ($wordSpaceAdjust!=0 || $wordSpaceAdjust != $this->wordSpaceAdjust)
2948 {
2949 $this->wordSpaceAdjust=$wordSpaceAdjust;
2950 $this->objects[$this->currentContents]['c'].=' '.sprintf('%.3F',$wordSpaceAdjust).' Tw';
2951 }
2952 $len = strlen($text);
2953 $start = 0;
2954 for ( $i = 0; $i < $len; $i++ )
2955 {
2956 $f = 1;
2957 $directiveArray = $this->PRVTcheckTextDirective( $text, $i, $f, true );
2958 $directive = $directiveArray['directive'];
2959 if ($directive)
2960 {
2961 // then we should write what we need to
2962 if ( $i > $start )
2963 {
2964 $part = substr($text,$start,$i-$start);
2965 $this->objects[$this->currentContents]['c'].=' /F'.$this->currentFontNum.' '.sprintf('%.1F',$size).' Tf ';
2966 $this->objects[$this->currentContents]['c'].=' ('.$this->filterText($part).') Tj';
2967 }
2968 if ( !$directiveArray['run_final'] )
2969 {
2970 // then there was nothing drastic done here, restore the contents
2971 $this->setCurrentFont();
2972 }
2973 else
2974 {
2975 $this->objects[$this->currentContents]['c'] .= ' ET';
2976 $f = 1;
2977 $xp = $x;
2978 $yp = $y;
2979 $directiveArray = $this->PRVTcheckTextDirective1( $text, $i, $f, 1, $xp, $yp, $size, $angle, $wordSpaceAdjust );
2980 if ( $directiveArray['y'] != 0 )
2981 {
2982 $returnArray['height'] = $y - $directiveArray['y'];
2983 }
2984
2985 $directive = $directiveArray['directive'];
2986
2987 // restart the text object
2988 if ( $angle == 0 )
2989 {
2990 $this->objects[$this->currentContents]['c'] .= "\n".'BT '.sprintf('%.3F',$xp).' '.sprintf('%.3F',$yp).' Td';
2991 }
2992 else
2993 {
2994 $a = deg2rad( (float)$angle );
2995 $tmp = "\n".'BT ';
2996 $tmp .= sprintf('%.3F',cos($a)).' '.sprintf('%.3F',(-1.0*sin($a))).' '.sprintf('%.3F',sin($a)).' '.sprintf('%.3F',cos($a)).' ';
2997 $tmp .= sprintf('%.3F',$xp).' '.sprintf('%.3F',$yp).' Tm';
2998 $this->objects[$this->currentContents]['c'] .= $tmp;
2999 }
3000 if ($wordSpaceAdjust!=0 || $wordSpaceAdjust != $this->wordSpaceAdjust)
3001 {
3002 $this->wordSpaceAdjust=$wordSpaceAdjust;
3003 $this->objects[$this->currentContents]['c'] .= ' '.sprintf('%.3F',$wordSpaceAdjust).' Tw';
3004 }
3005 }
3006 // and move the writing point to the next piece of text
3007 $i = $i + $directive - 1;
3008 $start = $i + 1;
3009 }
3010
3011 }
3012 if ( $start < $len )
3013 {
3014 $part = substr($text,$start);
3015 $this->objects[$this->currentContents]['c'].=' /F'.$this->currentFontNum.' '.sprintf('%.1F',$size).' Tf ';
3016 $this->objects[$this->currentContents]['c'].=' ('.$this->filterText($part).') Tj';
3017 }
3018 $this->objects[$this->currentContents]['c'].=' ET';
3019
3020 // if there are any open callbacks, then they should be called, to show the end of the line
3021 if ( $this->nCallback > 0 )
3022 {
3023 for ( $i = $this->nCallback; $i > 0; $i-- )
3024 {
3025 // call each function
3026 $tmp = $this->PRVTgetTextPosition( $x, $y, $angle, $size, $wordSpaceAdjust, $text );
3027 $info = array('x' => $tmp[0], 'y' => $tmp[1],
3028 'angle' => $angle,
3029 'status' => 'eol',
3030 'p' => $this->callback[$i]['p'],
3031 'nCallback' => $this->callback[$i]['nCallback'],
3032 'height' => $this->callback[$i]['height'],
3033 'decender' => $this->callback[$i]['decender'] );
3034 $func = $this->callback[$i]['f'];
3035 $this->$func( $info );
3036 }
3037 }
3038
3039 return $returnArray;
3040 }
3041
3046 function getTextWidth( $size, $text )
3047 {
3048 // this function should not change any of the settings, though it will need to
3049 // track any directives which change during calculation, so copy them at the start
3050 // and put them back at the end.
3051 $this->pushTextState( $this->currentTextState );
3052
3053 if (!$this->numFonts)
3054 {
3055 $this->selectFont('./fonts/Helvetica');
3056 }
3057
3058 // converts a number or a float to a string so it can get the width
3059 $text = "$text";
3060 // hmm, this is where it all starts to get tricky - use the font information to
3061 // calculate the width of each character, add them up and convert to user units
3062 $w = 0;
3063 $len = strlen( $text );
3064 $cf = $this->currentFont;
3065 for ( $i = 0; $i < $len; $i++ )
3066 {
3067 $f = 1;
3068 $directiveArray = $this->PRVTcheckTextDirective( $text, $i, $f );
3069 $directive = $directiveArray['directive'];
3070 if ( $directive )
3071 {
3072 if ($f)
3073 {
3074 $this->setCurrentFont();
3075 $cf = $this->currentFont;
3076 }
3077 $i = $i + $directive-1;
3078 }
3079 else
3080 {
3081 $char = ord( $text[$i] );
3082 if ( isset($this->fonts[$cf]['differences'][$char] ) )
3083 {
3084 // then this character is being replaced by another
3085 $name = $this->fonts[$cf]['differences'][$char];
3086 if ( isset($this->fonts[$cf]['C'][$name]['WX'] ) )
3087 {
3088 $w += $this->fonts[$cf]['C'][$name]['WX'];
3089 }
3090 }
3091 else if ( isset($this->fonts[$cf]['C'][$char]['WX'] ) )
3092 {
3093 $w += $this->fonts[$cf]['C'][$char]['WX'];
3094 }
3095 else
3096 {
3097 $w += 700;
3098 }
3099 }
3100 }
3101
3102 $this->popTextState();
3103 $this->setCurrentFont();
3104
3105 return $w*$size/1000;
3106 }
3107
3113 function PRVTadjustWrapText( $text, $actual, $width, &$x, &$adjust, $justification )
3114 {
3115 switch ( $justification )
3116 {
3117 case 'left':
3118 return;
3119 break;
3120 case 'right':
3121 $x += $width - $actual;
3122 break;
3123 case 'center':
3124 case 'centre':
3125 $x += ($width - $actual) / 2;
3126 break;
3127 case 'full':
3128 // count the number of words
3129 $words = explode( ' ', $text );
3130 $nspaces = count($words) - 1;
3131 if ( $nspaces > 0 )
3132 {
3133 $adjust = ($width-$actual) / $nspaces;
3134 }
3135 else
3136 {
3137 $adjust = 0;
3138 }
3139 break;
3140 }
3141 }
3142
3153 function addTextWrap( $x, $y, $width, $size, $text, $justification = 'left', $angle = 0, $test = 0 )
3154 {
3155 // this will display the text, and if it goes beyond the width $width, will backtrack to the
3156 // previous space or hyphen, and return the remainder of the text.
3157
3158 // $justification can be set to 'left','right','center','centre','full'
3159
3160 // need to store the initial text state, as this will change during the width calculation
3161 // but will need to be re-set before printing, so that the chars work out right
3162 $store_currentTextState = $this->currentTextState;
3163 $returnArray = array ( 'text' => '',
3164 'width' => 0,
3165 'height' => 0 );
3166 if ( $text != '' )
3167 {
3168 $returnArray['height'] = $this->getFontHeight( $size );
3169 }
3170
3171 if ( !$this->numFonts )
3172 {
3173 $this->selectFont('./fonts/Helvetica');
3174 }
3175 if ( $width <= 0 )
3176 {
3177 // error, pretend it printed ok, otherwise risking a loop
3178 return $returnArray;
3179 }
3180 $w = 0;
3181 $break = 0;
3182 $breakWidth = 0;
3183 $len = strlen( $text );
3184 $cf = $this->currentFont;
3185 $tw = $width/$size*1000;
3186 $directive = 0;
3187 for ( $i = 0; $i < $len; $i++ )
3188 {
3189 $f = 1;
3190 $directiveArray = $this->PRVTcheckTextDirective( $text, $i, $f );
3191 $deltaDirective = $directiveArray['directive'];
3192 $directive += $deltaDirective;
3193 if ( $directiveArray['y'] != 0 )
3194 {
3195 $y -= $directiveArray['y'];
3196 $returnArray['height'] = $directiveArray['y'];
3197 }
3198 if ($deltaDirective)
3199 {
3200 if ($f)
3201 {
3202 $this->setCurrentFont();
3203 $cf = $this->currentFont;
3204 }
3205 $i=$i+$deltaDirective-1;
3206 }
3207 else
3208 {
3209 $cOrd = ord($text[$i]);
3210 if ( isset( $this->fonts[$cf]['differences'][$cOrd] ) )
3211 {
3212 // then this character is being replaced by another
3213 $cOrd2 = $this->fonts[$cf]['differences'][$cOrd];
3214 }
3215 else
3216 {
3217 $cOrd2 = $cOrd;
3218 }
3219
3220 if ( isset( $this->fonts[$cf]['C'][$cOrd2]['WX'] ) )
3221 {
3222 $w += $this->fonts[$cf]['C'][$cOrd2]['WX'];
3223 }
3224 else
3225 {
3226 $w += 700;
3227 }
3228
3229 if ( $w > $tw )
3230 {
3231 // then we need to truncate this line
3232 if ( $break > 0 )
3233 {
3234 // then we have somewhere that we can split :)
3235 if ($text[$break] == ' ')
3236 {
3237 $tmp = substr( $text, 0, $break );
3238 }
3239 else
3240 {
3241 $tmp = substr( $text, 0, $break + 1 );
3242 }
3243 $adjust = 0;
3244 $this->PRVTadjustWrapText( $tmp, $breakWidth, $width, $x, $adjust, $justification );
3245
3246 // reset the text state
3247 $this->currentTextState = $store_currentTextState;
3248 $this->setCurrentFont();
3249 if ( !$test )
3250 {
3251 $addTextArray = $this->addText( $x, $y, $size, $tmp, $angle, $adjust );
3252 if ( $addTextArray['height'] != -1 &&
3253 $returnArray['height'] < $addTextArray['height'] )
3254 {
3255 $returnArray['height'] = $addTextArray['height'];
3256 }
3257 }
3258 $returnArray['text'] = substr( $text, $break + 1 );
3259 return $returnArray;
3260 }
3261 else
3262 {
3263 // just split before the current character
3264 $tmp = substr( $text, 0, $i );
3265 $adjust = 0;
3266 $ctmp = ord( $text[$i] );
3267 if ( isset($this->fonts[$cf]['differences'][$ctmp] ) )
3268 {
3269 $ctmp = $this->fonts[$cf]['differences'][$ctmp];
3270 }
3271 $tmpw = ( $w - $this->fonts[$cf]['C'][$ctmp]['WX'] ) * $size/1000;
3272 $this->PRVTadjustWrapText( $tmp, $tmpw, $width, $x, $adjust, $justification );
3273 // reset the text state
3274 $this->currentTextState = $store_currentTextState;
3275 $this->setCurrentFont();
3276 if ( !$test )
3277 {
3278 $addTextArray = $this->addText( $x, $y, $size, $tmp, $angle, $adjust );
3279 if ( $addTextArray['height'] != -1 &&
3280 $returnArray['height'] < $addTextArray['height'] )
3281 {
3282 $returnArray['height'] = $addTextArray['height'];
3283 }
3284 }
3285 $returnArray['text'] = substr( $text, $i );
3286 return $returnArray;
3287 }
3288 }
3289
3290 if ( $text[$i] == '-' )
3291 {
3292 $break = $i;
3293 $breakWidth = $w*$size/1000;
3294 }
3295 if ( $text[$i] == ' ' )
3296 {
3297 $break = $i;
3298 $ctmp = ord( $text[$i] );
3299 if ( isset($this->fonts[$cf]['differences'][$ctmp] ) )
3300 {
3301 $ctmp = $this->fonts[$cf]['differences'][$ctmp];
3302 }
3303 $breakWidth = ( $w - $this->fonts[$cf]['C'][$ctmp]['WX'] ) * $size/1000;
3304 }
3305 }
3306 }
3307 // then there was no need to break this line
3308 if ( $justification == 'full' )
3309 {
3310 $justification = 'left';
3311 }
3312 $adjust = 0;
3313 $tmpw = $w * $size/1000; //tmpw, text width
3314 $this->PRVTadjustWrapText( $text, $tmpw, $width, $x, $adjust, $justification );
3315 // reset the text state
3316 $this->currentTextState = $store_currentTextState;
3317 $this->setCurrentFont();
3318 if ( !$test )
3319 {
3320 $addTextArray = $this->addText( $x, $y, $size, $text, $angle, $adjust );
3321 if ( isset( $directive ) && $directive == $len )
3322 {
3323 return array( 'only_directive' => true );
3324 }
3325
3326 if ( $addTextArray['height'] != -1 &&
3327 $returnArray['height'] < $addTextArray['height'] )
3328 {
3329 $returnArray['height'] = $addTextArray['height'];
3330 }
3331 }
3332 $returnArray['width'] = $tmpw;
3333 return $returnArray;
3334 }
3335
3342 function saveState( $pageEnd = 0 )
3343 {
3344 if ( $pageEnd )
3345 {
3346 // this will be called at a new page to return the state to what it was on the
3347 // end of the previous page, before the stack was closed down
3348 // This is to get around not being able to have open 'q' across pages
3349 $opt = $this->stateStack[$pageEnd]; // ok to use this as stack starts numbering at 1
3350 $this->setColor($opt['col'],1);
3351 $this->setStrokeColor($opt['str'],1);
3352 $this->objects[$this->currentContents]['c'].="\n".$opt['lin'];
3353 //$this->currentLineStyle = $opt['lin'];
3354 }
3355 else
3356 {
3357 $this->nStateStack++;
3358 $this->stateStack[$this->nStateStack]=array( 'col' => $this->currentColour,
3359 'str' => $this->currentStrokeColour,
3360 'lin' => $this->currentLineStyle );
3361 }
3362 $this->objects[$this->currentContents]['c'] .= "\nq";
3363 }
3364
3368 function restoreState( $pageEnd = 0 )
3369 {
3370 if ( !$pageEnd )
3371 {
3372 $n = $this->nStateStack;
3373 $this->currentColour = $this->stateStack[$n]['col'];
3374 $this->currentStrokeColour = $this->stateStack[$n]['str'];
3375 $this->objects[$this->currentContents]['c'] .= "\n".$this->stateStack[$n]['lin'];
3376 $this->currentLineStyle = $this->stateStack[$n]['lin'];
3377 unset( $this->stateStack[$n] );
3378 $this->nStateStack--;
3379 }
3380 $this->objects[$this->currentContents]['c'] .= "\nQ";
3381 }
3382
3389 function openObject()
3390 {
3391 $this->nStack++;
3392 $this->stack[$this->nStack] = array( 'c' => $this->currentContents,
3393 'p' => $this->currentPage );
3394 // add a new object of the content type, to hold the data flow
3395 $this->numObj++;
3396 $this->o_contents( $this->numObj, 'new' );
3397 $this->currentContents = $this->numObj;
3398 $this->looseObjects[$this->numObj] = 1;
3399
3400 return $this->numObj;
3401 }
3402
3406 function reopenObject( $id )
3407 {
3408 $this->nStack++;
3409 $this->stack[$this->nStack] = array( 'c' => $this->currentContents,
3410 'p' => $this->currentPage );
3411 $this->currentContents=$id;
3412 // also if this object is the primary contents for a page, then set the current page to its parent
3413 if ( isset( $this->objects[$id]['onPage'] ) )
3414 {
3415 $this->currentPage = $this->objects[$id]['onPage'];
3416 }
3417 }
3418
3422 function closeObject()
3423 {
3424 // close the object, as long as there was one open in the first place, which will be indicated by
3425 // an objectId on the stack.
3426 if ( $this->nStack > 0 )
3427 {
3428 $this->currentContents=$this->stack[$this->nStack]['c'];
3429 $this->currentPage=$this->stack[$this->nStack]['p'];
3430 $this->nStack--;
3431 // easier to probably not worry about removing the old entries, they will be overwritten
3432 // if there are new ones.
3433 }
3434 }
3435
3439 function stopObject( $id )
3440 {
3441 // if an object has been appearing on pages up to now, then stop it, this page will
3442 // be the last one that could contian it.
3443 if ( isset( $this->addLooseObjects[$id] ) )
3444 {
3445 $this->addLooseObjects[$id] = '';
3446 }
3447 }
3448
3452 function addObject( $id, $options = 'add' )
3453 {
3454 // add the specified object to the page
3455 if ( isset( $this->looseObjects[$id] ) && $this->currentContents != $id )
3456 {
3457 // then it is a valid object, and it is not being added to itself
3458 switch ( $options )
3459 {
3460 case 'all':
3461 // then this object is to be added to this page (done in the next block) and
3462 // all future new pages.
3463 $this->addLooseObjects[$id] = 'all';
3464 case 'add':
3465 if ( isset( $this->objects[$this->currentContents]['onPage'] ) )
3466 {
3467 // then the destination contents is the primary for the page
3468 // (though this object is actually added to that page)
3469 $this->o_page( $this->objects[$this->currentContents]['onPage'], 'content', $id );
3470 }
3471 break;
3472 case 'even':
3473 $this->addLooseObjects[$id] = 'even';
3474 $pageObjectId = $this->objects[$this->currentContents]['onPage'];
3475 if ( $this->objects[$pageObjectId]['info']['pageNum'] % 2 == 0 )
3476 {
3477 $this->addObject( $id ); // hacky huh :)
3478 }
3479 break;
3480 case 'odd':
3481 $this->addLooseObjects[$id] = 'odd';
3482 $pageObjectId = $this->objects[$this->currentContents]['onPage'];
3483 if ( $this->objects[$pageObjectId]['info']['pageNum'] % 2 == 1 )
3484 {
3485 $this->addObject( $id ); // hacky huh :)
3486 }
3487 break;
3488 case 'next':
3489 $this->addLooseObjects[$id] = 'all';
3490 break;
3491 case 'nexteven':
3492 $this->addLooseObjects[$id] = 'even';
3493 break;
3494 case 'nextodd':
3495 $this->addLooseObjects[$id] = 'odd';
3496 break;
3497 }
3498 }
3499 }
3500
3504 function addInfo( $label, $value = 0 )
3505 {
3506 // this will only work if the label is one of the valid ones.
3507 // modify this so that arrays can be passed as well.
3508 // if $label is an array then assume that it is key=>value pairs
3509 // else assume that they are both scalar, anything else will probably error
3510 if ( is_array( $label ) )
3511 {
3512 foreach ( $label as $l => $v )
3513 {
3514 $this->o_info( $this->infoObject, $l, $v );
3515 }
3516 }
3517 else
3518 {
3519 $this->o_info( $this->infoObject, $label, $value );
3520 }
3521 }
3522
3526 function setPreferences( $label, $value = 0 )
3527 {
3528 // this will only work if the label is one of the valid ones.
3529 if ( is_array( $label ) )
3530 {
3531 foreach ( $label as $l => $v )
3532 {
3533 $this->o_catalog( $this->catalogId, 'viewerPreferences', array( $l => $v ) );
3534 }
3535 }
3536 else
3537 {
3538 $this->o_catalog( $this->catalogId, 'viewerPreferences', array( $label => $value ) );
3539 }
3540 }
3541
3547 function PRVT_getBytes( &$data, $pos, $num )
3548 {
3549 // return the integer represented by $num bytes from $pos within $data
3550 $ret = 0;
3551 for ( $i = 0; $i < $num; $i++ )
3552 {
3553 $ret = $ret * 256;
3554 $ret+= ord( $data[$pos+$i] );
3555 }
3556 return $ret;
3557 }
3558
3565 function addPngFromFile( $file, $x, $y, $w = 0, $h = 0 )
3566 {
3567 // read in a png file, interpret it, then add to the system
3568 $error = 0;
3569 $fp = @fopen( $file, 'rb' );
3570 if ( $fp )
3571 {
3572 $data = '';
3573 while ( !feof( $fp ) )
3574 {
3575 $data .= fread( $fp, 1024 );
3576 }
3577 fclose( $fp );
3578 }
3579 else
3580 {
3581 $error = 1;
3582 $errormsg = 'trouble opening file: '.$file;
3583 }
3584
3585 if ( !$error )
3586 {
3587 $header = chr(137).chr(80).chr(78).chr(71).chr(13).chr(10).chr(26).chr(10);
3588 if ( substr( $data, 0, 8 ) != $header )
3589 {
3590 $error = 1;
3591 $errormsg = 'this file does not have a valid header';
3592 }
3593 }
3594
3595 if ( !$error )
3596 {
3597 // set pointer
3598 $p = 8;
3599 $len = strlen($data);
3600 // cycle through the file, identifying chunks
3601 $haveHeader = 0;
3602 $info = array();
3603 $idata = '';
3604 $pdata = '';
3605 while ( $p < $len )
3606 {
3607 $chunkLen = $this->PRVT_getBytes($data,$p,4);
3608 $chunkType = substr($data,$p+4,4);
3609 //echo $chunkType.' - '.$chunkLen.'<br>';
3610
3611 switch ( $chunkType )
3612 {
3613 case 'IHDR':
3614 // this is where all the file information comes from
3615 $info['width'] = $this->PRVT_getBytes( $data, $p+8, 4 );
3616 $info['height'] = $this->PRVT_getBytes( $data, $p+12, 4);
3617 $info['bitDepth'] = ord( $data[$p+16] );
3618 $info['colorType'] = ord( $data[$p+17] );
3619 $info['compressionMethod'] = ord( $data[$p+18] );
3620 $info['filterMethod'] = ord( $data[$p+19] );
3621 $info['interlaceMethod'] = ord( $data[$p+20] );
3622 //print_r($info);
3623 $haveHeader = 1;
3624 if ( $info['compressionMethod'] != 0 )
3625 {
3626 $error = 1;
3627 $errormsg = 'unsupported compression method';
3628 }
3629 if ( $info['filterMethod'] != 0 )
3630 {
3631 $error = 1;
3632 $errormsg = 'unsupported filter method';
3633 }
3634 break;
3635
3636 case 'PLTE':
3637 $pdata .= substr( $data, $p + 8, $chunkLen );
3638 break;
3639 case 'IDAT':
3640 $idata .= substr( $data, $p + 8, $chunkLen );
3641 break;
3642 case 'tRNS':
3643 //this chunk can only occur once and it must occur after the PLTE chunk and before IDAT chunk
3644 //print "tRNS found, color type = ".$info['colorType']."<BR>";
3645 $transparency = array();
3646 if ( $info['colorType'] == 3 )
3647 {
3648 // indexed color, rbg
3649 /* corresponding to entries in the plte chunk
3650 Alpha for palette index 0: 1 byte
3651 Alpha for palette index 1: 1 byte
3652 ...etc...
3653 */
3654 // there will be one entry for each palette entry. up until the last non-opaque entry.
3655 // set up an array, stretching over all palette entries which will be o (opaque) or 1 (transparent)
3656 $transparency['type'] = 'indexed';
3657 $numPalette = strlen($pdata)/3;
3658 $trans = 0;
3659 for ( $i = $chunkLen; $i >= 0; $i-- )
3660 {
3661 if ( ord( $data[$p+8+$i] ) == 0 )
3662 {
3663 $trans = $i;
3664 }
3665 }
3666 $transparency['data'] = $trans;
3667
3668 }
3669 elseif ( $info['colorType'] == 0 )
3670 {
3671 // grayscale
3672 /* corresponding to entries in the plte chunk
3673 Gray: 2 bytes, range 0 .. (2^bitdepth)-1
3674 */
3675 //$transparency['grayscale']=$this->PRVT_getBytes($data,$p+8,2); // g = grayscale
3676 $transparency['type'] = 'indexed';
3677 $transparency['data'] = ord( $data[$p+8+1] );
3678
3679 }
3680 elseif ( $info['colorType'] == 2)
3681 {
3682 // truecolor
3683 /* corresponding to entries in the plte chunk
3684 Red: 2 bytes, range 0 .. (2^bitdepth)-1
3685 Green: 2 bytes, range 0 .. (2^bitdepth)-1
3686 Blue: 2 bytes, range 0 .. (2^bitdepth)-1
3687 */
3688 $transparency['r'] = $this->PRVT_getBytes( $data, $p+8, 2 ); // r from truecolor
3689 $transparency['g'] = $this->PRVT_getBytes( $data, $p+10 ,2 ); // g from truecolor
3690 $transparency['b'] = $this->PRVT_getBytes( $data, $p+12 ,2 ); // b from truecolor
3691 }
3692 else
3693 {
3694 //unsupported transparency type
3695 }
3696 // KS End new code
3697 break;
3698 default:
3699 break;
3700 }
3701
3702 $p += $chunkLen+12;
3703 }
3704
3705 if ( !$haveHeader )
3706 {
3707 $error = 1;
3708 $errormsg = 'information header is missing';
3709 }
3710 if ( isset( $info['interlaceMethod']) && $info['interlaceMethod'] )
3711 {
3712 $error = 1;
3713 $errormsg = 'There appears to be no support for interlaced images in pdf.';
3714 }
3715 }
3716
3717 if ( !$error && $info['bitDepth'] > 8 )
3718 {
3719 $error = 1;
3720 $errormsg = 'only bit depth of 8 or less is supported';
3721 }
3722
3723 if ( !$error )
3724 {
3725 if ( $info['colorType'] != 2 && $info['colorType'] != 0 && $info['colorType'] != 3 )
3726 {
3727 $error = 1;
3728 $errormsg = 'transparancey alpha channel not supported, transparency only supported for palette images.';
3729 }
3730 else
3731 {
3732 switch ( $info['colorType'] )
3733 {
3734 case 3:
3735 $color = 'DeviceRGB';
3736 $ncolor = 1;
3737 break;
3738 case 2:
3739 $color = 'DeviceRGB';
3740 $ncolor = 3;
3741 break;
3742 case 0:
3743 $color = 'DeviceGray';
3744 $ncolor = 1;
3745 break;
3746 }
3747 }
3748 }
3749 if ( $error )
3750 {
3751 eZDebug::writeError( 'Adding PNG file, '. $file .', to PDF failed: '.$errormsg, 'Cpdf::addPngFromFile' );
3752 $this->addMessage('PNG error - ('.$file.') '.$errormsg);
3753 return false;
3754 }
3755 if ( $w == 0 )
3756 {
3757 $w = $h/$info['height'] * $info['width'];
3758 }
3759 if ( $h == 0 )
3760 {
3761 $h = $w * $info['height'] / $info['width'];
3762 }
3763 //print_r($info);
3764 // so this image is ok... add it in.
3765 $this->numImages++;
3766 $im = $this->numImages;
3767 $label = 'I'.$im;
3768 $this->numObj++;
3769 // $this->o_image($this->numObj,'new',array('label'=>$label,'data'=>$idata,'iw'=>$w,'ih'=>$h,'type'=>'png','ic'=>$info['width']));
3770 $options = array( 'label' => $label,
3771 'data' => $idata,
3772 'bitsPerComponent' => $info['bitDepth'],
3773 'pdata' => $pdata,
3774 'iw' => $info['width'],
3775 'ih' => $info['height'],
3776 'type' => 'png',
3777 'color' => $color,
3778 'ncolor' => $ncolor );
3779
3780 if ( isset( $transparency ) )
3781 {
3782 $options['transparency'] = $transparency;
3783 }
3784 $this->o_image( $this->numObj, 'new', $options );
3785
3786 $this->objects[$this->currentContents]['c'].="\nq";
3787 $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3F',$w)." 0 0 ".sprintf('%.3F',$h)." ".sprintf('%.3F',$x)." ".sprintf('%.3F',$y)." cm";
3788 $this->objects[$this->currentContents]['c'].="\n/".$label.' Do';
3789 $this->objects[$this->currentContents]['c'].="\nQ";
3790 }
3791
3795 function addJpegFromFile( $img, $x, $y, $w = 0, $h = 0 )
3796 {
3797 // attempt to add a jpeg image straight from a file, using no GD commands
3798 // note that this function is unable to operate on a remote file.
3799
3800 if ( !file_exists( $img ) )
3801 {
3802 //echo "FILE NOT EXISTS: $img";
3803 return;
3804 }
3805
3806 $tmp = getimagesize( $img );
3807 $imageWidth = $tmp[0];
3808 $imageHeight = $tmp[1];
3809
3810 if ( isset( $tmp['channels'] ) )
3811 {
3812 $channels = $tmp['channels'];
3813 }
3814 else
3815 {
3816 $channels = 3;
3817 }
3818
3819 if ( $w <= 0 && $h <= 0 )
3820 {
3821 $w = $imageWidth;
3822 }
3823 if ( $w == 0 )
3824 {
3825 $w = $h / $imageHeight * $imageWidth;
3826 }
3827 if ( $h == 0 )
3828 {
3829 $h = $w * $imageHeight / $imageWidth;
3830 }
3831
3832 $fp = fopen( $img, 'rb' );
3833
3834 $data = fread( $fp, filesize( $img ) );
3835
3836 fclose( $fp );
3837
3838 $this->addJpegImage_common( $data, $x, $y, $w, $h, $imageWidth, $imageHeight, $channels );
3839 }
3840
3846 function addImage( &$img, $x, $y, $w = 0, $h = 0, $quality = 75 )
3847 {
3848 // add a new image into the current location, as an external object
3849 // add the image at $x,$y, and with width and height as defined by $w & $h
3850
3851 // note that this will only work with full colour images and makes them jpg images for display
3852 // later versions could present lossless image formats if there is interest.
3853
3854 // there seems to be some problem here in that images that have quality set above 75 do not appear
3855 // not too sure why this is, but in the meantime I have restricted this to 75.
3856 if ( $quality > 75 )
3857 {
3858 $quality = 75;
3859 }
3860
3861 // if the width or height are set to zero, then set the other one based on keeping the image
3862 // height/width ratio the same, if they are both zero, then give up :)
3863 $imageWidth = imagesx( $img );
3864 $imageHeight = imagesy( $img );
3865
3866 if ( $w <= 0 && $h <= 0 )
3867 {
3868 return;
3869 }
3870 if ( $w == 0 )
3871 {
3872 $w = $h / $imageHeight * $imageWidth;
3873 }
3874 if ( $h == 0 )
3875 {
3876 $h = $w * $imageHeight / $imageWidth;
3877 }
3878
3879 // gotta get the data out of the img..
3880
3881 // so I write to a temp file, and then read it back.. soo ugly, my apologies.
3882 $tmpDir = '/tmp';
3883 $tmpName = tempnam( $tmpDir, 'img' );
3884 imagejpeg( $img, $tmpName, $quality );
3885
3886 $fp = fopen( $tmpName, 'rb' );
3887 if ( $fp )
3888 {
3889 $data = '';
3890 while ( !feof( $fp ) )
3891 {
3892 $data .= fread( $fp, 1024 );
3893 }
3894 fclose( $fp );
3895 }
3896 else
3897 {
3898 $error = 1;
3899 $errormsg = 'trouble opening file';
3900 }
3901 unlink( $tmpName );
3902 $this->addJpegImage_common( $data, $x, $y, $w, $h, $imageWidth, $imageHeight );
3903 }
3904
3910 function addJpegImage_common( &$data, $x, $y, $w = 0, $h = 0, $imageWidth, $imageHeight, $channels = 3 )
3911 {
3912 // note that this function is not to be called externally
3913 // it is just the common code between the GD and the file options
3914 $this->numImages++;
3915 $im = $this->numImages;
3916 $label = 'I'.$im;
3917 $this->numObj++;
3918 $this->o_image( $this->numObj, 'new', array( 'label' => $label,
3919 'data' => $data,
3920 'iw' => $imageWidth,
3921 'ih' => $imageHeight,
3922 'channels' => $channels ) );
3923
3924 $this->objects[$this->currentContents]['c'] .= "\nq";
3925 $this->objects[$this->currentContents]['c'] .= "\n".sprintf('%.3F',$w)." 0 0 ".sprintf('%.3F',$h)." ".sprintf('%.3F',$x)." ".sprintf('%.3F',$y)." cm";
3926 $this->objects[$this->currentContents]['c'] .= "\n/".$label.' Do';
3927 $this->objects[$this->currentContents]['c'] .= "\nQ";
3928 }
3929
3933 function openHere( $style, $a = 0, $b = 0, $c = 0 )
3934 {
3935 // this function will open the document at a specified page, in a specified style
3936 // the values for style, and the required paramters are:
3937 // 'XYZ' left, top, zoom
3938 // 'Fit'
3939 // 'FitH' top
3940 // 'FitV' left
3941 // 'FitR' left,bottom,right
3942 // 'FitB'
3943 // 'FitBH' top
3944 // 'FitBV' left
3945 $this->numObj++;
3946 $this->o_destination( $this->numObj, 'new', array( 'page' => $this->currentPage,
3947 'type' => $style,
3948 'p1' => $a, 'p2' => $b, 'p3' => $c ) );
3949 $id = $this->catalogId;
3950 $this->o_catalog( $id, 'openHere', $this->numObj );
3951 }
3952
3956 function addDestination($label,$style,$a=0,$b=0,$c=0)
3957 {
3958 // associates the given label with the destination, it is done this way so that a destination can be specified after
3959 // it has been linked to
3960 // styles are the same as the 'openHere' function
3961 $this->numObj++;
3962 $this->o_destination( $this->numObj, 'new', array( 'page' => $this->currentPage,
3963 'type' => $style,
3964 'p1' => $a, 'p2' => $b, 'p3' => $c ) );
3965 $id = $this->numObj;
3966 // store the label->idf relationship, note that this means that labels can be used only once
3967 $this->destinations["$label"] = $id;
3968 }
3969
3975 function setFontFamily($family,$options='')
3976 {
3977 if (!is_array($options))
3978 {
3979 if ($family=='init')
3980 {
3981 // set the known family groups
3982 // these font families will be used to enable bold and italic markers to be included
3983 // within text streams. html forms will be used... <b></b> <i></i>
3984 $this->fontFamilies['Helvetica'] = array(
3985 'b'=>'Helvetica-Bold'
3986 ,'i'=>'Helvetica-Oblique'
3987 ,'bi'=>'Helvetica-BoldOblique'
3988 ,'ib'=>'Helvetica-BoldOblique'
3989 );
3990 $this->fontFamilies['Courier'] = array(
3991 'b'=>'Courier-Bold'
3992 ,'i'=>'Courier-Oblique'
3993 ,'bi'=>'Courier-BoldOblique'
3994 ,'ib'=>'Courier-BoldOblique'
3995 );
3996 $this->fontFamilies['Times-Roman'] = array(
3997 'b'=>'Times-Bold'
3998 ,'i'=>'Times-Italic'
3999 ,'bi'=>'Times-BoldItalic'
4000 ,'ib'=>'Times-BoldItalic'
4001 );
4002 }
4003 }
4004 else
4005 {
4006 // the user is trying to set a font family
4007 // note that this can also be used to set the base ones to something else
4008 if (strlen($family))
4009 {
4010 $this->fontFamilies[$family] = $options;
4011 }
4012 }
4013 }
4014
4018 function addMessage( $message )
4019 {
4020 $this->messages .= $message."\n";
4021 }
4022
4026 function transaction( $action )
4027 {
4028 switch ( $action )
4029 {
4030 case 'start':
4031 // store all the data away into the checkpoint variable
4032 $data = get_object_vars( $this );
4033 $this->checkpoint = $data;
4034 unset( $data );
4035 break;
4036 case 'commit':
4037 if ( is_array( $this->checkpoint ) && isset( $this->checkpoint['checkpoint'] ) )
4038 {
4039 $tmp = $this->checkpoint['checkpoint'];
4040 $this->checkpoint = $tmp;
4041 unset( $tmp );
4042 }
4043 else
4044 {
4045 $this->checkpoint = '';
4046 }
4047 break;
4048 case 'rewind':
4049 // do not destroy the current checkpoint, but move us back to the state then, so that we can try again
4050 if ( is_array( $this->checkpoint ) )
4051 {
4052 // can only abort if were inside a checkpoint
4053 $tmp = $this->checkpoint;
4054 foreach ( $tmp as $k => $v )
4055 {
4056 if ( $k != 'checkpoint' )
4057 {
4058 $this->$k = $v;
4059 }
4060 }
4061 unset($tmp);
4062 }
4063 break;
4064 case 'abort':
4065 if ( is_array( $this->checkpoint ) )
4066 {
4067 // can only abort if were inside a checkpoint
4068 $tmp = $this->checkpoint;
4069 foreach ( $tmp as $k => $v )
4070 {
4071 $this->$k = $v;
4072 }
4073 unset($tmp);
4074 }
4075 break;
4076 }
4077
4078 }
4079
4088 function compareCMYK( $cmykArray1, $cmykArray2 )
4089 {
4090 return ( $cmykArray1['c'] == $cmykArray2['c'] &&
4091 $cmykArray1['m'] == $cmykArray2['m'] &&
4092 $cmykArray1['y'] == $cmykArray2['y'] &&
4093 $cmykArray1['k'] == $cmykArray2['k'] );
4094 }
4095
4101 function pushTextState( $newState )
4102 {
4103 $this->textStateStack[] = $this->currentTextState;
4104 $this->currentTextState = $newState;
4105 $this->setCurrentFont();
4106 }
4107
4111 function popTextState( )
4112 {
4113 $this->currentTextState = array_pop( $this->textStateStack );
4114 $this->setCurrentFont();
4115 }
4116
4117 public $textStateStack = array( '' );
4118 } // end of class
4119
4120 ?>
Cpdf
Cpdf provides.
Definition: class.pdf.php:18
Cpdf\polygon
polygon( $p, $np, $f=0)
Definition: class.pdf.php:2420
Cpdf\md5_16
md5_16($string)
Definition: class.pdf.php:1540
Cpdf\$encryptionKey
$encryptionKey
Definition: class.pdf.php:206
Cpdf\setColor
setColor( $cmykArray, $force=0)
Definition: class.pdf.php:2215
Cpdf\$currentStrokeColour
$currentStrokeColour
Definition: class.pdf.php:89
Cpdf\pushTextState
pushTextState( $newState)
Definition: class.pdf.php:4101
Cpdf\addPngFromFile
addPngFromFile( $file, $x, $y, $w=0, $h=0)
Definition: class.pdf.php:3565
Cpdf\$nStack
$nStack
Definition: class.pdf.php:120
Cpdf\$numPages
$numPages
Definition: class.pdf.php:110
Cpdf\addText
addText( $x, $y, $size, $text, $angle=0, $wordSpaceAdjust=0)
Definition: class.pdf.php:2909
Cpdf\$currentTextState
$currentTextState
Definition: class.pdf.php:176
Cpdf\stopObject
stopObject( $id)
Definition: class.pdf.php:3439
Cpdf\$numObj
$numObj
Definition: class.pdf.php:28
Cpdf\restoreState
restoreState( $pageEnd=0)
Definition: class.pdf.php:3368
Cpdf\o_fontDescriptor
o_fontDescriptor( $id, $action, $options='')
Definition: class.pdf.php:957
Cpdf\o_contents
o_contents($id, $action, $options='')
Definition: class.pdf.php:1344
Cpdf\$currentPage
$currentPage
Definition: class.pdf.php:69
Cpdf\o_catalog
o_catalog( $id, $action, $options='')
Definition: class.pdf.php:353
Cpdf\compareCMYK
compareCMYK( $cmykArray1, $cmykArray2)
Definition: class.pdf.php:4088
Cpdf\$objects
$objects
Definition: class.pdf.php:33
Cpdf\addJpegFromFile
addJpegFromFile( $img, $x, $y, $w=0, $h=0)
Definition: class.pdf.php:3795
Cpdf\shadedRectangle
shadedRectangle( $x1, $y1, $width, $height, $options)
Definition: class.pdf.php:2450
Cpdf\addLink
addLink($url, $x0, $y0, $x1, $y1)
Definition: class.pdf.php:1618
Cpdf\addJpegImage_common
addJpegImage_common(&$data, $x, $y, $w=0, $h=0, $imageWidth, $imageHeight, $channels=3)
Definition: class.pdf.php:3910
Cpdf\$arc4
$arc4
Definition: class.pdf.php:186
Cpdf\$numFonts
$numFonts
Definition: class.pdf.php:79
Cpdf\setColorRGB
setColorRGB( $r, $g, $b, $force=0)
Definition: class.pdf.php:2205
Cpdf\o_destination
o_destination( $id, $action, $options='')
Definition: class.pdf.php:271
Cpdf\encryptInit
encryptInit($id)
Definition: class.pdf.php:1552
Cpdf\o_pattern
o_pattern( $id, $action, $options='')
Definition: class.pdf.php:667
Cpdf\curve
curve( $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
Definition: class.pdf.php:2262
Cpdf\o_function
o_function( $id, $action, $options='')
Definition: class.pdf.php:585
Cpdf\filledEllipse
filledEllipse( $x0, $y0, $r1, $r2=0, $angle=0, $nSeg=8, $astart=0, $afinish=360)
Definition: class.pdf.php:2281
Cpdf\openFont
openFont( $font)
Definition: class.pdf.php:1762
Cpdf\setCurrentFont
setCurrentFont()
Definition: class.pdf.php:2153
Cpdf\$callback
$callback
Definition: class.pdf.php:211
Cpdf\$encrypted
$encrypted
Definition: class.pdf.php:201
Cpdf\stream
stream( $options='')
Definition: class.pdf.php:2546
Cpdf\o_info
o_info( $id, $action, $options='')
Definition: class.pdf.php:1102
Cpdf\addContent
addContent( $content)
Definition: class.pdf.php:2197
Cpdf\$firstPageId
$firstPageId
Definition: class.pdf.php:153
Cpdf\getFontHeight
getFontHeight( $size=false)
Definition: class.pdf.php:2593
Cpdf\addTextWrap
addTextWrap( $x, $y, $width, $size, $text, $justification='left', $angle=0, $test=0)
Definition: class.pdf.php:3153
Cpdf\output
output($debug=0)
Definition: class.pdf.php:1674
Cpdf\PRVTcheckTextDirective1
PRVTcheckTextDirective1(&$text, $i, &$f, $final, &$x, &$y, $size=0, $angle=0, $wordSpaceAdjust=0)
Definition: class.pdf.php:2693
Cpdf\$fileIdentifier
$fileIdentifier
Definition: class.pdf.php:196
Cpdf\$stateStack
$stateStack
Definition: class.pdf.php:100
Cpdf\addObject
addObject( $id, $options='add')
Definition: class.pdf.php:3452
Cpdf\$ez
$ez
Definition: class.pdf.php:20
Cpdf\o_shading
o_shading( $id, $action, $options='')
Definition: class.pdf.php:748
Cpdf\$messages
$messages
Definition: class.pdf.php:181
Cpdf\$procsetObjectId
$procsetObjectId
Definition: class.pdf.php:164
Cpdf\filledRectangle
filledRectangle( $x1, $y1, $width, $height)
Definition: class.pdf.php:2470
Cpdf\newDocument
newDocument($pageSize=array(0, 0, 612, 792))
Definition: class.pdf.php:1723
Cpdf\newPage
newPage( $insert=0, $id=0, $pos='after')
Definition: class.pdf.php:2488
Cpdf\getFontDecender
getFontDecender( $size=false)
Definition: class.pdf.php:2613
Cpdf\getTextWidth
getTextWidth( $size, $text)
Definition: class.pdf.php:3046
Cpdf\getFirstPageId
getFirstPageId()
Definition: class.pdf.php:2187
Cpdf\openObject
openObject()
Definition: class.pdf.php:3389
Cpdf\$currentContents
$currentContents
Definition: class.pdf.php:74
Cpdf\$fonts
$fonts
Definition: class.pdf.php:44
Cpdf\$currentFont
$currentFont
Definition: class.pdf.php:49
Cpdf\$destinations
$destinations
Definition: class.pdf.php:222
Cpdf\o_image
o_image($id, $action, $options='')
Definition: class.pdf.php:1394
Cpdf\$fontFamilies
$fontFamilies
Definition: class.pdf.php:171
Cpdf\$nCallBack
$nCallBack
Definition: class.pdf.php:24
Cpdf\o_outlines
o_outlines( $id, $action, $options='')
Definition: class.pdf.php:543
Cpdf\o_procset
o_procset( $id, $action, $options='')
Definition: class.pdf.php:1061
Cpdf\$infoObject
$infoObject
Definition: class.pdf.php:137
Cpdf\$currentLineStyle
$currentLineStyle
Definition: class.pdf.php:94
Cpdf\o_encryption
o_encryption($id, $action, $options='')
Definition: class.pdf.php:1469
Cpdf\$catalogId
$catalogId
Definition: class.pdf.php:38
Cpdf\$currentBaseFont
$currentBaseFont
Definition: class.pdf.php:54
Cpdf\PRVTadjustWrapText
PRVTadjustWrapText( $text, $actual, $width, &$x, &$adjust, $justification)
Definition: class.pdf.php:3113
Cpdf\o_viewerPreferences
o_viewerPreferences( $id, $action, $options='')
Definition: class.pdf.php:311
Cpdf\checkAllHere
checkAllHere()
Definition: class.pdf.php:1668
Cpdf\__construct
__construct( $pageSize=array(0, 0, 612, 792))
Definition: class.pdf.php:236
Cpdf\setEncryption
setEncryption($userPass='', $ownerPass='', $pc=array())
Definition: class.pdf.php:1638
Cpdf\filterText
filterText( $text)
Definition: class.pdf.php:2634
Cpdf\setLineStyle
setLineStyle( $width=1, $cap='', $join='', $dash='', $phase=0)
Definition: class.pdf.php:2385
Cpdf\addDestination
addDestination($label, $style, $a=0, $b=0, $c=0)
Definition: class.pdf.php:3956
Cpdf\addInfo
addInfo( $label, $value=0)
Definition: class.pdf.php:3504
Cpdf\transaction
transaction( $action)
Definition: class.pdf.php:4026
Cpdf\$currentColour
$currentColour
Definition: class.pdf.php:84
Cpdf\$textStateStack
$textStateStack
Definition: class.pdf.php:4117
Cpdf\line
line( $x1, $y1, $x2, $y2)
Definition: class.pdf.php:2254
Cpdf\setFontFamily
setFontFamily($family, $options='')
Definition: class.pdf.php:3975
Cpdf\ARC4
ARC4($text)
Definition: class.pdf.php:1592
Cpdf\ellipse
ellipse( $x0, $y0, $r1, $r2=0, $angle=0, $nSeg=8, $astart=0, $afinish=360, $close=1, $fill=0)
Definition: class.pdf.php:2296
Cpdf\$stack
$stack
Definition: class.pdf.php:115
Cpdf\addMessage
addMessage( $message)
Definition: class.pdf.php:4018
Cpdf\setStrokeColor
setStrokeColor( $cmykArray, $force=0)
Definition: class.pdf.php:2240
Cpdf\o_page
o_page($id, $action, $options='')
Definition: class.pdf.php:1275
Cpdf\o_action
o_action( $id, $action, $options='')
Definition: class.pdf.php:1156
Cpdf\ARC4_init
ARC4_init($key='')
Definition: class.pdf.php:1566
Cpdf\selectFont
selectFont( $fontName, $encoding='', $set=1)
Definition: class.pdf.php:1897
Cpdf\setPreferences
setPreferences( $label, $value=0)
Definition: class.pdf.php:3526
Cpdf\addImage
addImage(&$img, $x, $y, $w=0, $h=0, $quality=75)
Definition: class.pdf.php:3846
Cpdf\o_fontEncoding
o_fontEncoding( $id, $action, $options='')
Definition: class.pdf.php:1014
Cpdf\o_annotation
o_annotation( $id, $action, $options='')
Definition: class.pdf.php:1216
Cpdf\reopenObject
reopenObject( $id)
Definition: class.pdf.php:3406
Cpdf\openHere
openHere( $style, $a=0, $b=0, $c=0)
Definition: class.pdf.php:3933
Cpdf\currentFont
currentFont()
Definition: class.pdf.php:2136
Cpdf\closeObject
closeObject()
Definition: class.pdf.php:3422
Cpdf\o_font
o_font( $id, $action, $options='')
Definition: class.pdf.php:851
Cpdf\$looseObjects
$looseObjects
Definition: class.pdf.php:126
Cpdf\o_pages
o_pages( $id, $action, $options='')
Definition: class.pdf.php:409
Cpdf\saveState
saveState( $pageEnd=0)
Definition: class.pdf.php:3342
Cpdf\$checkpoint
$checkpoint
Definition: class.pdf.php:229
Cpdf\$options
$options
Definition: class.pdf.php:148
Cpdf\$addLooseObjects
$addLooseObjects
Definition: class.pdf.php:131
Cpdf\$arc4_objnum
$arc4_objnum
Definition: class.pdf.php:191
Cpdf\addInternalLink
addInternalLink($label, $x0, $y0, $x1, $y1)
Definition: class.pdf.php:1627
Cpdf\$wordSpaceAdjust
$wordSpaceAdjust
Definition: class.pdf.php:159
Cpdf\popTextState
popTextState()
Definition: class.pdf.php:4111
Cpdf\$currentFontNum
$currentFontNum
Definition: class.pdf.php:59
Cpdf\setStrokeColorRGB
setStrokeColorRGB( $r, $g, $b, $force=0)
Definition: class.pdf.php:2229
Cpdf\rectangle
rectangle( $x1, $y1, $width, $height)
Definition: class.pdf.php:2479
Cpdf\$nCallback
$nCallback
Definition: class.pdf.php:216
Cpdf\PRVTcheckTextDirective
PRVTcheckTextDirective(&$text, $i, &$f, $final=0)
Definition: class.pdf.php:2677
Cpdf\$nStateStack
$nStateStack
Definition: class.pdf.php:105
Cpdf\PRVTgetTextPosition
PRVTgetTextPosition( $x, $y, $angle, $size, $wa, $text)
Definition: class.pdf.php:2654
Cpdf\$numImages
$numImages
Definition: class.pdf.php:142
Cpdf\partEllipse
partEllipse( $x0, $y0, $astart, $afinish, $r1, $r2=0, $angle=0, $nSeg=8)
Definition: class.pdf.php:2273
Cpdf\$currentNode
$currentNode
Definition: class.pdf.php:64
Cpdf\PRVT_getBytes
PRVT_getBytes(&$data, $pos, $num)
Definition: class.pdf.php:3547
eZDebug\writeError
static writeError( $string, $label="", $backgroundClass="")
Definition: ezdebug.php:545
eZExecution\cleanExit
static cleanExit()
Definition: ezexecution.php:100
eZMath\rgbToCMYK
static rgbToCMYK( $rgbArray)
Definition: ezmath.php:49
eZMath\rgbToCMYK2
static rgbToCMYK2( $r, $g, $b)
Definition: ezmath.php:81
Copyright © 1998 – 2026 7x. All rights reserved. Licensed under the GNU GPLv2+.