Two cosmetic fixes for the event editor.
[openemr.git] / library / html2pdf / fpdi / fpdi.php
blobf6ba91f96debdb9f07097a32e6295cbf25600c64
1 <?php
2 //
3 // FPDI - Version 1.4.2
4 //
5 // Copyright 2004-2011 Setasign - Jan Slabon
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
11 // http://www.apache.org/licenses/LICENSE-2.0
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
20 define('FPDI_VERSION', '1.4.2');
22 // Check for TCPDF and remap TCPDF to FPDF
23 if (class_exists('TCPDF', false)) {
24 require_once('fpdi2tcpdf_bridge.php');
27 require_once('fpdf_tpl.php');
28 require_once('fpdi_pdf_parser.php');
31 class FPDI extends FPDF_TPL {
32 /**
33 * Actual filename
34 * @var string
36 var $current_filename;
38 /**
39 * Parser-Objects
40 * @var array
42 var $parsers;
44 /**
45 * Current parser
46 * @var object
48 var $current_parser;
50 /**
51 * object stack
52 * @var array
54 var $_obj_stack;
56 /**
57 * done object stack
58 * @var array
60 var $_don_obj_stack;
62 /**
63 * Current Object Id.
64 * @var integer
66 var $_current_obj_id;
68 /**
69 * The name of the last imported page box
70 * @var string
72 var $lastUsedPageBox;
74 /**
75 * Cache for imported pages/template ids
76 * @var array
78 var $_importedPages = array();
80 /**
81 * Set a source-file
83 * @param string $filename a valid filename
84 * @return int number of available pages
86 function setSourceFile($filename) {
87 $this->current_filename = $filename;
89 if (!isset($this->parsers[$filename]))
90 $this->parsers[$filename] = $this->_getPdfParser($filename);
91 $this->current_parser =& $this->parsers[$filename];
93 return $this->parsers[$filename]->getPageCount();
96 /**
97 * Returns a PDF parser object
99 * @param string $filename
100 * @return fpdi_pdf_parser
102 function _getPdfParser($filename) {
103 return new fpdi_pdf_parser($filename, $this);
107 * Get the current PDF version
109 * @return string
111 function getPDFVersion() {
112 return $this->PDFVersion;
116 * Set the PDF version
118 * @return string
120 function setPDFVersion($version = '1.3') {
121 $this->PDFVersion = $version;
125 * Import a page
127 * @param int $pageno pagenumber
128 * @return int Index of imported page - to use with fpdf_tpl::useTemplate()
130 function importPage($pageno, $boxName = '/CropBox') {
131 if ($this->_intpl) {
132 return $this->error('Please import the desired pages before creating a new template.');
135 $fn = $this->current_filename;
137 // check if page already imported
138 $pageKey = $fn . '-' . ((int)$pageno) . $boxName;
139 if (isset($this->_importedPages[$pageKey]))
140 return $this->_importedPages[$pageKey];
142 $parser =& $this->parsers[$fn];
143 $parser->setPageno($pageno);
145 if (!in_array($boxName, $parser->availableBoxes))
146 return $this->Error(sprintf('Unknown box: %s', $boxName));
148 $pageboxes = $parser->getPageBoxes($pageno, $this->k);
151 * MediaBox
152 * CropBox: Default -> MediaBox
153 * BleedBox: Default -> CropBox
154 * TrimBox: Default -> CropBox
155 * ArtBox: Default -> CropBox
157 if (!isset($pageboxes[$boxName]) && ($boxName == '/BleedBox' || $boxName == '/TrimBox' || $boxName == '/ArtBox'))
158 $boxName = '/CropBox';
159 if (!isset($pageboxes[$boxName]) && $boxName == '/CropBox')
160 $boxName = '/MediaBox';
162 if (!isset($pageboxes[$boxName]))
163 return false;
165 $this->lastUsedPageBox = $boxName;
167 $box = $pageboxes[$boxName];
169 $this->tpl++;
170 $this->tpls[$this->tpl] = array();
171 $tpl =& $this->tpls[$this->tpl];
172 $tpl['parser'] =& $parser;
173 $tpl['resources'] = $parser->getPageResources();
174 $tpl['buffer'] = $parser->getContent();
175 $tpl['box'] = $box;
177 // To build an array that can be used by PDF_TPL::useTemplate()
178 $this->tpls[$this->tpl] = array_merge($this->tpls[$this->tpl], $box);
180 // An imported page will start at 0,0 everytime. Translation will be set in _putformxobjects()
181 $tpl['x'] = 0;
182 $tpl['y'] = 0;
184 // handle rotated pages
185 $rotation = $parser->getPageRotation($pageno);
186 $tpl['_rotationAngle'] = 0;
187 if (isset($rotation[1]) && ($angle = $rotation[1] % 360) != 0) {
188 $steps = $angle / 90;
190 $_w = $tpl['w'];
191 $_h = $tpl['h'];
192 $tpl['w'] = $steps % 2 == 0 ? $_w : $_h;
193 $tpl['h'] = $steps % 2 == 0 ? $_h : $_w;
195 if ($angle < 0)
196 $angle += 360;
198 $tpl['_rotationAngle'] = $angle * -1;
201 $this->_importedPages[$pageKey] = $this->tpl;
203 return $this->tpl;
207 * Returns the last used page box
209 * @return string
211 function getLastUsedPageBox() {
212 return $this->lastUsedPageBox;
216 function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0, $adjustPageSize = false) {
217 if ($adjustPageSize == true && is_null($_x) && is_null($_y)) {
218 $size = $this->getTemplateSize($tplidx, $_w, $_h);
219 $orientation = $size['w'] > $size['h'] ? 'L' : 'P';
220 $size = array($size['w'], $size['h']);
222 if (is_subclass_of($this, 'TCPDF')) {
223 $this->setPageFormat($size, $orientation);
224 } else {
225 $size = $this->_getpagesize($size);
227 if($orientation!=$this->CurOrientation || $size[0]!=$this->CurPageSize[0] || $size[1]!=$this->CurPageSize[1])
229 // New size or orientation
230 if($orientation=='P')
232 $this->w = $size[0];
233 $this->h = $size[1];
235 else
237 $this->w = $size[1];
238 $this->h = $size[0];
240 $this->wPt = $this->w*$this->k;
241 $this->hPt = $this->h*$this->k;
242 $this->PageBreakTrigger = $this->h-$this->bMargin;
243 $this->CurOrientation = $orientation;
244 $this->CurPageSize = $size;
245 $this->PageSizes[$this->page] = array($this->wPt, $this->hPt);
250 $this->_out('q 0 J 1 w 0 j 0 G 0 g'); // reset standard values
251 $s = parent::useTemplate($tplidx, $_x, $_y, $_w, $_h);
252 $this->_out('Q');
254 return $s;
258 * Private method, that rebuilds all needed objects of source files
260 function _putimportedobjects() {
261 if (is_array($this->parsers) && count($this->parsers) > 0) {
262 foreach($this->parsers AS $filename => $p) {
263 $this->current_parser =& $this->parsers[$filename];
264 if (isset($this->_obj_stack[$filename]) && is_array($this->_obj_stack[$filename])) {
265 while(($n = key($this->_obj_stack[$filename])) !== null) {
266 $nObj = $this->current_parser->pdf_resolve_object($this->current_parser->c, $this->_obj_stack[$filename][$n][1]);
268 $this->_newobj($this->_obj_stack[$filename][$n][0]);
270 if ($nObj[0] == PDF_TYPE_STREAM) {
271 $this->pdf_write_value($nObj);
272 } else {
273 $this->pdf_write_value($nObj[1]);
276 $this->_out('endobj');
277 $this->_obj_stack[$filename][$n] = null; // free memory
278 unset($this->_obj_stack[$filename][$n]);
279 reset($this->_obj_stack[$filename]);
288 * Private Method that writes the form xobjects
290 function _putformxobjects() {
291 $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
292 reset($this->tpls);
293 foreach($this->tpls AS $tplidx => $tpl) {
294 $p=($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
295 $this->_newobj();
296 $cN = $this->n; // TCPDF/Protection: rem current "n"
298 $this->tpls[$tplidx]['n'] = $this->n;
299 $this->_out('<<' . $filter . '/Type /XObject');
300 $this->_out('/Subtype /Form');
301 $this->_out('/FormType 1');
303 $this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]',
304 (isset($tpl['box']['llx']) ? $tpl['box']['llx'] : $tpl['x']) * $this->k,
305 (isset($tpl['box']['lly']) ? $tpl['box']['lly'] : -$tpl['y']) * $this->k,
306 (isset($tpl['box']['urx']) ? $tpl['box']['urx'] : $tpl['w'] + $tpl['x']) * $this->k,
307 (isset($tpl['box']['ury']) ? $tpl['box']['ury'] : $tpl['h'] - $tpl['y']) * $this->k
310 $c = 1;
311 $s = 0;
312 $tx = 0;
313 $ty = 0;
315 if (isset($tpl['box'])) {
316 $tx = -$tpl['box']['llx'];
317 $ty = -$tpl['box']['lly'];
319 if ($tpl['_rotationAngle'] <> 0) {
320 $angle = $tpl['_rotationAngle'] * M_PI/180;
321 $c=cos($angle);
322 $s=sin($angle);
324 switch($tpl['_rotationAngle']) {
325 case -90:
326 $tx = -$tpl['box']['lly'];
327 $ty = $tpl['box']['urx'];
328 break;
329 case -180:
330 $tx = $tpl['box']['urx'];
331 $ty = $tpl['box']['ury'];
332 break;
333 case -270:
334 $tx = $tpl['box']['ury'];
335 $ty = -$tpl['box']['llx'];
336 break;
339 } else if ($tpl['x'] != 0 || $tpl['y'] != 0) {
340 $tx = -$tpl['x'] * 2;
341 $ty = $tpl['y'] * 2;
344 $tx *= $this->k;
345 $ty *= $this->k;
347 if ($c != 1 || $s != 0 || $tx != 0 || $ty != 0) {
348 $this->_out(sprintf('/Matrix [%.5F %.5F %.5F %.5F %.5F %.5F]',
349 $c, $s, -$s, $c, $tx, $ty
353 $this->_out('/Resources ');
355 if (isset($tpl['resources'])) {
356 $this->current_parser =& $tpl['parser'];
357 $this->pdf_write_value($tpl['resources']); // "n" will be changed
358 } else {
359 $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
360 if (isset($this->_res['tpl'][$tplidx]['fonts']) && count($this->_res['tpl'][$tplidx]['fonts'])) {
361 $this->_out('/Font <<');
362 foreach($this->_res['tpl'][$tplidx]['fonts'] as $font)
363 $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
364 $this->_out('>>');
366 if(isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) ||
367 isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls']))
369 $this->_out('/XObject <<');
370 if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images'])) {
371 foreach($this->_res['tpl'][$tplidx]['images'] as $image)
372 $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
374 if (isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) {
375 foreach($this->_res['tpl'][$tplidx]['tpls'] as $i => $tpl)
376 $this->_out($this->tplprefix . $i . ' ' . $tpl['n'] . ' 0 R');
378 $this->_out('>>');
380 $this->_out('>>');
383 $nN = $this->n; // TCPDF: rem new "n"
384 $this->n = $cN; // TCPDF: reset to current "n"
385 if (is_subclass_of($this, 'TCPDF')) {
386 $p = $this->_getrawstream($p);
387 $this->_out('/Length ' . strlen($p) . ' >>');
388 $this->_out("stream\n" . $p . "\nendstream");
389 } else {
390 $this->_out('/Length ' . strlen($p) . ' >>');
391 $this->_putstream($p);
393 $this->_out('endobj');
394 $this->n = $nN; // TCPDF: reset to new "n"
397 $this->_putimportedobjects();
401 * Rewritten to handle existing own defined objects
403 function _newobj($obj_id = false, $onlynewobj = false) {
404 if (!$obj_id) {
405 $obj_id = ++$this->n;
408 //Begin a new object
409 if (!$onlynewobj) {
410 $this->offsets[$obj_id] = is_subclass_of($this, 'TCPDF') ? $this->bufferlen : strlen($this->buffer);
411 $this->_out($obj_id . ' 0 obj');
412 $this->_current_obj_id = $obj_id; // for later use with encryption
415 return $obj_id;
419 * Writes a value
420 * Needed to rebuild the source document
422 * @param mixed $value A PDF-Value. Structure of values see cases in this method
424 function pdf_write_value(&$value)
426 if (is_subclass_of($this, 'TCPDF')) {
427 parent::pdf_write_value($value);
430 switch ($value[0]) {
432 case PDF_TYPE_TOKEN:
433 $this->_straightOut($value[1] . ' ');
434 break;
435 case PDF_TYPE_NUMERIC:
436 case PDF_TYPE_REAL:
437 if (is_float($value[1]) && $value[1] != 0) {
438 $this->_straightOut(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') . ' ');
439 } else {
440 $this->_straightOut($value[1] . ' ');
442 break;
444 case PDF_TYPE_ARRAY:
446 // An array. Output the proper
447 // structure and move on.
449 $this->_straightOut('[');
450 for ($i = 0; $i < count($value[1]); $i++) {
451 $this->pdf_write_value($value[1][$i]);
454 $this->_out(']');
455 break;
457 case PDF_TYPE_DICTIONARY:
459 // A dictionary.
460 $this->_straightOut('<<');
462 reset ($value[1]);
464 while (list($k, $v) = each($value[1])) {
465 $this->_straightOut($k . ' ');
466 $this->pdf_write_value($v);
469 $this->_straightOut('>>');
470 break;
472 case PDF_TYPE_OBJREF:
474 // An indirect object reference
475 // Fill the object stack if needed
476 $cpfn =& $this->current_parser->filename;
478 if (!isset($this->_don_obj_stack[$cpfn][$value[1]])) {
479 $this->_newobj(false, true);
480 $this->_obj_stack[$cpfn][$value[1]] = array($this->n, $value);
481 $this->_don_obj_stack[$cpfn][$value[1]] = array($this->n, $value); // Value is maybee obsolete!!!
483 $objid = $this->_don_obj_stack[$cpfn][$value[1]][0];
485 $this->_out($objid . ' 0 R');
486 break;
488 case PDF_TYPE_STRING:
490 // A string.
491 $this->_straightOut('(' . $value[1] . ')');
493 break;
495 case PDF_TYPE_STREAM:
497 // A stream. First, output the
498 // stream dictionary, then the
499 // stream data itself.
500 $this->pdf_write_value($value[1]);
501 $this->_out('stream');
502 $this->_out($value[2][1]);
503 $this->_out('endstream');
504 break;
506 case PDF_TYPE_HEX:
507 $this->_straightOut('<' . $value[1] . '>');
508 break;
510 case PDF_TYPE_BOOLEAN:
511 $this->_straightOut($value[1] ? 'true ' : 'false ');
512 break;
514 case PDF_TYPE_NULL:
515 // The null object.
517 $this->_straightOut('null ');
518 break;
524 * Modified so not each call will add a newline to the output.
526 function _straightOut($s) {
527 if (!is_subclass_of($this, 'TCPDF')) {
528 if($this->state==2)
529 $this->pages[$this->page] .= $s;
530 else
531 $this->buffer .= $s;
532 } else {
533 if ($this->state == 2) {
534 if (isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
535 // puts data before page footer
536 $page = substr($this->getPageBuffer($this->page), 0, -$this->footerlen[$this->page]);
537 $footer = substr($this->getPageBuffer($this->page), -$this->footerlen[$this->page]);
538 $this->setPageBuffer($this->page, $page . ' ' . $s . "\n" . $footer);
539 } else {
540 $this->setPageBuffer($this->page, $s, true);
542 } else {
543 $this->setBuffer($s);
549 * rewritten to close opened parsers
552 function _enddoc() {
553 parent::_enddoc();
554 $this->_closeParsers();
558 * close all files opened by parsers
560 function _closeParsers() {
561 if ($this->state > 2 && count($this->parsers) > 0) {
562 foreach ($this->parsers as $k => $_){
563 $this->parsers[$k]->closeFile();
564 $this->parsers[$k] = null;
565 unset($this->parsers[$k]);
567 return true;
569 return false;