Projet

Général

Profil

Paste
Télécharger (34,5 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / views_pdf / views_pdf_template.php @ 7547bb19

1
<?php
2

    
3
/**
4
 * @file
5
 * PDF Class to generate PDFs with native PHP. This class based on FPDF and FPDI.
6
 *
7
 * A direct include of this class is not realy possible. The basic functions of drupal must be
8
 * present.
9
 *
10
 */
11

    
12

    
13
/**
14
 * Get the depending classes.
15
 */
16
require_once views_pdf_get_library('tcpdf') . '/tcpdf.php';
17

    
18
if (file_exists(views_pdf_get_library('fpdi') . '/fpdi_bridge.php')) {
19
  require_once views_pdf_get_library('fpdi') . '/fpdi_bridge.php';
20
}
21
else {
22
  require_once views_pdf_get_library('fpdi') . '/fpdi2tcpdf_bridge.php';
23
}
24

    
25
require_once views_pdf_get_library('fpdi') . '/fpdi.php';
26

    
27

    
28
/**
29
 * The main class to generate the PDF.
30
 */
31
class PdfTemplate extends FPDI {
32
  protected static $fontList = NULL;
33
  protected static $fontListClean = NULL;
34
  protected static $templateList = NULL;
35
  protected static $hyphenatePatterns = NULL;
36
  protected $defaultFontStyle = '';
37
  protected $defaultFontFamily = 'helvetica';
38
  protected $defaultFontSize = '11';
39
  protected $defaultTextAlign = 'L';
40
  protected $defaultFontColor = '000000';
41
  protected $defaultPageTemplateFiles = array();
42
  protected $mainContentPageNumber = 0;
43
  protected $rowContentPageNumber = 0;
44
  protected $defaultOrientation = 'P';
45
  protected $defaultFormat = 'A4';
46
  protected $addNewPageBeforeNextContent = FALSE;
47
  protected $elements = array();
48
  protected $headerFooterData = array();
49
  protected $views_header = '';
50
  protected $view = NULL;
51
  protected $views_footer = '';
52
  protected $headerFooterOptions = array();
53
  protected $lastWritingPage = 1;
54
  protected $lastWritingPositions;
55

    
56
  protected static $defaultFontList = array(
57
    'almohanad' => 'AlMohanad',
58
    'arialunicid0' => 'ArialUnicodeMS',
59
    'courier' => 'Courier',
60
    'courierb' => 'Courier Bold',
61
    'courierbi' => 'Courier Bold Italic',
62
    'courieri' => 'Courier Italic',
63
    'dejavusans' => 'DejaVuSans',
64
    'dejavusansb' => 'DejaVuSans-Bold',
65
    'dejavusansbi' => 'DejaVuSans-BoldOblique',
66
    'dejavusansi' => 'DejaVuSans-Oblique',
67
    'dejavusanscondensed' => 'DejaVuSansCondensed',
68
    'dejavusanscondensedb' => 'DejaVuSansCondensed-Bold',
69
    'dejavusanscondensedbi' => 'DejaVuSansCondensed-BoldOblique',
70
    'dejavusanscondensedi' => 'DejaVuSansCondensed-Oblique',
71
    'dejavusansmono' => 'DejaVuSansMono',
72
    'dejavusansmonob' => 'DejaVuSansMono-Bold',
73
    'dejavusansmonobi' => 'DejaVuSansMono-BoldOblique',
74
    'dejavusansmonoi' => 'DejaVuSansMono-Oblique',
75
    'dejavuserif' => 'DejaVuSerif',
76
    'dejavuserifb' => 'DejaVuSerif-Bold',
77
    'dejavuserifbi' => 'DejaVuSerif-BoldItalic',
78
    'dejavuserifi' => 'DejaVuSerif-Italic',
79
    'dejavuserifcondensed' => 'DejaVuSerifCondensed',
80
    'dejavuserifcondensedb' => 'DejaVuSerifCondensed-Bold',
81
    'dejavuserifcondensedbi' => 'DejaVuSerifCondensed-BoldItalic',
82
    'dejavuserifcondensedi' => 'DejaVuSerifCondensed-Italic',
83
    'freemono' => 'FreeMono',
84
    'freemonob' => 'FreeMonoBold',
85
    'freemonobi' => 'FreeMonoBoldOblique',
86
    'freemonoi' => 'FreeMonoOblique',
87
    'freesans' => 'FreeSans',
88
    'freesansb' => 'FreeSansBold',
89
    'freesansbi' => 'FreeSansBoldOblique',
90
    'freesansi' => 'FreeSansOblique',
91
    'freeserif' => 'FreeSerif',
92
    'freeserifb' => 'FreeSerifBold',
93
    'freeserifbi' => 'FreeSerifBoldItalic',
94
    'freeserifi' => 'FreeSerifItalic',
95
    'hysmyeongjostdmedium' => 'HYSMyeongJoStd-Medium-Acro',
96
    'helvetica' => 'Helvetica',
97
    'helveticab' => 'Helvetica Bold',
98
    'helveticabi' => 'Helvetica Bold Italic',
99
    'helveticai' => 'Helvetica Italic',
100
    'kozgopromedium' => 'KozGoPro-Medium-Acro',
101
    'kozminproregular' => 'KozMinPro-Regular-Acro',
102
    'msungstdlight' => 'MSungStd-Light-Acro',
103
    'stsongstdlight' => 'STSongStd-Light-Acro',
104
    'symbol' => 'Symbol',
105
    'times' => 'Times New Roman',
106
    'timesb' => 'Times New Roman Bold',
107
    'timesbi' => 'Times New Roman Bold Italic',
108
    'timesi' => 'Times New Roman Italic',
109
    'zapfdingbats' => 'Zapf Dingbats',
110
    'zarbold' => 'ZarBold'
111
  );
112

    
113
  /**
114
   * This method overrides the parent constructor method.
115
   * this is need to reset the default values.
116
   */
117
  public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=TRUE, $encoding='UTF-8', $diskcache=FALSE) {
118
    parent::__construct($orientation, $unit, $format, $unicode, $encoding, $diskcache);
119
    $this->defaultOrientation = $orientation;
120
    $this->defaultFormat = $format;
121
  }
122

    
123
  public function setDefaultFontSize($size) {
124
    $this->defaultFontSize = $size;
125
  }
126

    
127
  public function setDefaultFontFamily($family) {
128
    $this->defaultFontFamily = $family;
129
  }
130

    
131
  public function setDefaultFontStyle($style) {
132
    $this->defaultFontStyle = $style;
133
  }
134

    
135
  public function setDefaultTextAlign($style) {
136
    $this->defaultTextAlign = $style;
137
  }
138

    
139
  public function setDefaultFontColor($color) {
140
    $this->defaultFontColor = $color;
141
  }
142

    
143
  public function setDefaultPageTemplate($path, $key, $pageNumbering = 'main') {
144
    $this->defaultPageTemplateFiles[$key] = array(
145
      'path' => $path,
146
      'numbering' => $pageNumbering
147
    );
148
  }
149

    
150
  public function setViewsHeader($header) {
151
    $this->views_header = $header;
152
  }
153
  /**
154
   * This method must be overriden, in the other case, some
155
   * output is printed to the header.
156
   */
157
  function Header() {
158
    if (!empty($this->views_header)) {
159
      $this->writeHTML($this->views_header);
160
    }
161
  }
162

    
163
  public function setViewsFooter($footer) {
164
    $this->views_footer = $footer;
165
   }
166

    
167
  /**
168
   * This method must be overriden, in the other case, some
169
   * output is printed to the footer.
170
   */
171
  function Footer() {
172
    $this->SetY(-$this->bMargin);
173
    if (!empty($this->views_footer)) {
174
      $this->writeHTML($this->views_footer);
175
    }
176
  }
177

    
178
  /**
179
   * Converts a hex color into an array with RGB colors.
180
   */
181
  public function convertHexColorToArray($hex) {
182
    if (drupal_strlen($hex) == 6) {
183
      $r = drupal_substr($hex, 0, 2);
184
      $g = drupal_substr($hex, 2, 2);
185
      $b = drupal_substr($hex, 4, 2);
186
      return array(hexdec($r), hexdec($g), hexdec($b));
187

    
188
    }
189
    elseif (drupal_strlen($hex) == 3) {
190
      $r = drupal_substr($hex, 0, 1);
191
      $g = drupal_substr($hex, 1, 1);
192
      $b = drupal_substr($hex, 2, 1);
193
      return array(hexdec($r), hexdec($g), hexdec($b));
194

    
195
    }
196
    else {
197
      return array();
198
    }
199
  }
200

    
201
  /**
202
   * Parse color input into an array.
203
   *
204
   * @param string $color
205
   *   Color entered by the user
206
   *
207
   * @return array
208
   *   Color as an array
209
   */
210
  public function parseColor($color) {
211
    $color = trim($color, ', ');
212
    $components = explode(',', $color);
213
    if (count($components) == 1) {
214
      return $this->convertHexColorToArray($color);
215
    }
216
    else {
217
      // Remove white spaces from comonents:
218
      foreach ($components as $id => $component) {
219
        $components[$id] = trim($component);
220
      }
221
      return $components;
222
    }
223
  }
224

    
225
  /**
226
   * This method draws a field on the PDF.
227
   */
228
  public function drawContent($row, $options, &$view = NULL, $key = NULL, $printLabels = TRUE) {
229

    
230
    if (!is_array($options)) {
231
      $options = array();
232
    }
233

    
234
    // Set defaults:
235
    $options += array(
236
      'position' => array(),
237
      'text' => array(),
238
      'render' => array(),
239
    );
240

    
241
    $options['position'] += array(
242
      'corner' => 'top_left',
243
      'x' => 0,
244
      'y' => 0,
245
      'object' => 'last_position',
246
      'width' => 0,
247
      'height' => 0,
248
    );
249

    
250
    $options['text'] += array(
251
      'font_family' => 'default',
252
      'font_style' => '',
253
    );
254

    
255
    $options['render'] += array(
256
      'eval_before' => '',
257
      'eval_after' => '',
258
      'bypass_eval_before' => FALSE,
259
      'bypass_eval_after' => FALSE,
260
      'custom_layout'     => FALSE,
261
      'custom_post'       => FALSE,
262
    );
263

    
264
    $x = $y = 0;
265

    
266

    
267
    // Get the page dimensions
268
    $pageDim = $this->getPageDimensions();
269

    
270
    // Check if there is a minimum space defined. If so, then ensure
271
    // that we have enough space left on this page. If not force adding
272
    // a new one.
273
    if (isset($options['render']['minimal_space'])) {
274
      $enoughtSpace = ($this->y + $this->bMargin + $options['render']['minimal_space']) < $pageDim['hk'];
275
    }
276
    else {
277
      $enoughtSpace = TRUE;
278
    }
279

    
280

    
281
    // Check if there is a page, if not add it:
282
    if (!$enoughtSpace OR $this->getPage() == 0 OR $this->addNewPageBeforeNextContent == TRUE) {
283
      $this->addNewPageBeforeNextContent = FALSE;
284
      $this->addPage();
285
    }
286

    
287
    // Get the page dimenstions again, because it can be that a new
288
    // page was added with new dimensions.
289
    $pageDim = $this->getPageDimensions();
290

    
291
    // Determine the last writting y coordinate, if we are on a new
292
    // page we need to reset it back to the top margin.
293
    if ($this->lastWritingPage != $this->getPage() OR ($this->y + $this->bMargin) > $pageDim['hk']) {
294
      $last_writing_y_position = $this->tMargin;
295
    }
296
    else {
297
      $last_writing_y_position = $this->y;
298
    }
299

    
300
    // Determin the x and y coordinates
301
    if ($options['position']['object'] == 'last_position') {
302
      $x = $this->x + $options['position']['x'];
303
      $y = $this->y + $options['position']['y'];
304
    }
305
    elseif ($options['position']['object'] == 'page') {
306
      switch ($options['position']['corner']) {
307
        default:
308
        case 'top_left':
309
          $x = $options['position']['x']+$this->lMargin;
310
          $y = $options['position']['y']+$this->tMargin;
311
          break;
312

    
313
        case 'top_right':
314
          $x = $options['position']['x'] + $pageDim['wk'] - $this->rMargin;
315
          $y = $options['position']['y'] + $this->tMargin;
316
          break;
317

    
318
        case 'bottom_left':
319
          $x = $options['position']['x'] + $this->rMargin;
320
          $y = $options['position']['y'] + $pageDim['hk'] - $this->bMargin;
321

    
322
          break;
323

    
324
        case 'bottom_right':
325
          $x = $options['position']['x'] + $pageDim['wk'] - $this->rMargin;
326
          $y = $options['position']['y'] + $pageDim['hk'] - $this->bMargin;
327

    
328
          break;
329
      }
330
    }
331
    elseif (
332
      $options['position']['object'] == 'self' or
333
      //$options['position']['object'] == 'last' or
334
      preg_match('/field\_(.*)/', $options['position']['object'], $rs)
335
    ) {
336
      if ($options['position']['object'] == 'last') {
337
        $relative_to_element = $this->lastWritingElement;
338
      }
339
      elseif ($options['position']['object'] == 'self') {
340
        $relative_to_element = $key;
341
      }
342
      else {
343
        $relative_to_element = $rs[1];
344
      }
345

    
346
      if (isset($this->elements[$relative_to_element])) {
347

    
348
        switch ($options['position']['corner']) {
349
          default:
350
          case 'top_left':
351
            $x = $options['position']['x'] + $this->elements[$relative_to_element]['x'];
352
            $y = $options['position']['y'] + $this->elements[$relative_to_element]['y'];
353
            break;
354

    
355
          case 'top_right':
356
            $x = $options['position']['x'] + $this->elements[$relative_to_element]['x'] + $this->elements[$relative_to_element]['width'];
357
            $y = $options['position']['y'] + $this->elements[$relative_to_element]['y'];
358
            break;
359

    
360
          case 'bottom_left':
361
            $x = $options['position']['x'] + $this->elements[$relative_to_element]['x'];
362
            $y = $options['position']['y'] + $this->elements[$relative_to_element]['y'] + $this->elements[$relative_to_element]['height'];
363

    
364
            break;
365

    
366
          case 'bottom_right':
367
            $x = $options['position']['x'] + $this->elements[$relative_to_element]['x'] + $this->elements[$relative_to_element]['width'];
368
            $y = $options['position']['y'] + $this->elements[$relative_to_element]['y'] + $this->elements[$relative_to_element]['height'];
369

    
370
            break;
371
        }
372

    
373
        // Handle if the relative element is on another page. So using the
374
        // the last writing position instead for y.
375
        if ($this->getPage() != $this->elements[$relative_to_element]['page'] && $options['position']['object'] != 'self') {
376
          $this->setPage($this->elements[$relative_to_element]['page']);
377
        }
378
        elseif ($this->getPage() != $this->elements[$relative_to_element]['page'] && $options['position']['object'] == 'self') {
379
          $y = $y - $this->elements[$relative_to_element]['y'] + $last_writing_y_position;
380
          $this->SetPage($this->lastWritingPage);
381
        }
382

    
383
      }
384
      else {
385
        $x = $this->x;
386
        $y = $last_writing_y_position;
387
      }
388

    
389
    }
390

    
391
    // No position match (for example header/footer)
392
    else {
393
      // Render or return
394
      if (is_object($view) && $key != NULL ) {
395
        $content = $view->field[$key]->theme($row);
396
      }
397
      else{
398
        return;
399
      }
400

    
401
    }
402

    
403
    if ($key !== NULL && $view->field[$key]->theme($row) || !empty($row)) {
404
      $this->SetX($x);
405
      $this->SetY($y);
406
      $this->renderRow($x, $y, $row, $options, $view, $key, $printLabels);
407
    }
408
  }
409

    
410
  protected function renderRow($x, $y, $row, $options, &$view = NULL, $key = NULL, $printLabels = TRUE) {
411
      if ($options['position']['object'] !== 'header_footer') {
412
        $pageDim = $this->getPageDimensions();
413

    
414
        // Render the content if it is not already:
415
        if (is_object($view) && $key != NULL && isset($view->field[$key]) && is_object($view->field[$key]) && !is_string($row)) {
416
          $content = $view->field[$key]->theme($row);
417
        }
418
        elseif (is_string($row)) {
419
          $content = $row;
420
        }
421
        else {
422
          // We got bad data. So return.
423
          return;
424
        }
425

    
426
        if (empty($key) || !empty($view->field[$key]->options['exclude']) || (empty($content) && $view->field[$key]->options['hide_empty'])) {
427
          return '';
428
        }
429

    
430
        // Apply the hyphenation patterns to the content:
431
        if (!isset($options['text']['hyphenate']) && is_object($view) && is_object($view->display_handler)) {
432
          $options['text']['hyphenate'] = $view->display_handler->get_option('default_text_hyphenate');
433
        }
434

    
435
        if (isset($options['text']['hyphenate']) && $options['text']['hyphenate'] != 'none') {
436
          $patternFile = $options['text']['hyphenate'];
437
          if ($options['text']['hyphenate'] == 'auto' && is_object($row)) {
438

    
439
            // Workaround:
440
            // Since "$nodeLanguage = $row->node_language;" does not work anymore,
441
            // we using this:
442
            if (isset($row->_field_data['nid']['entity']->language)) {
443
              $nodeLanguage = $row->_field_data['nid']['entity']->language;
444

    
445
              foreach (self::getAvailableHyphenatePatterns() as $file => $pattern) {
446
                if (stristr($pattern, $nodeLanguage) !== FALSE) {
447
                  $patternFile = $file;
448
                  break;
449
                }
450
              }
451
            }
452
          }
453

    
454
          $patternFile = views_pdf_get_library('tcpdf') . '/hyphenate_patterns/' . $patternFile;
455

    
456
          if (file_exists($patternFile)) {
457
            if (method_exists('TCPDF_STATIC', 'getHyphenPatternsFromTEX')) {
458
              $hyphen_patterns = TCPDF_STATIC::getHyphenPatternsFromTEX($patternFile);
459
            }
460
            else {
461
              $hyphen_patterns = $this->getHyphenPatternsFromTEX($patternFile);
462
            }
463

    
464
            // Bugfix if you like to print some html code to the PDF, we
465
            // need to prevent the replacement of this tags.
466
            $content = str_replace('&gt;', '&amp;gt;', $content);
467
            $content = str_replace('&lt;', '&amp;lt;', $content);
468
            $content = $this->hyphenateText($content, $hyphen_patterns);
469

    
470
          }
471
        }
472

    
473
        // Set css variable
474
        if (is_object($view) && is_object($view->display_handler)) {
475
          $css_file = $view->display_handler->get_option('css_file');
476
        }
477

    
478
        // Render Labels
479
        $prefix = '';
480
        if ($printLabels && !empty($view->field[$key]->options['label'])) {
481
          $prefix = $view->field[$key]->options['label'];
482
          if ($view->field[$key]->options['element_label_colon']) {
483
            $prefix .= ':';
484
          }
485
          $prefix .= ' ';
486
        }
487

    
488
        $font_size = empty($options['text']['font_size']) ? $this->defaultFontSize : $options['text']['font_size'] ;
489
        $font_family = ($options['text']['font_family'] == 'default' || empty($options['text']['font_family'])) ? $this->defaultFontFamily : $options['text']['font_family'];
490
        $font_style = is_array($options['text']['font_style']) ? $options['text']['font_style'] : $this->defaultFontStyle;
491
        $textColor = !empty($options['text']['color']) ? $this->parseColor($options['text']['color']) : $this->parseColor($this->defaultFontColor);
492

    
493

    
494
        $w = $options['position']['width'];
495
        $h = $options['position']['height'];
496
        $border = 0;
497
        $align = isset($options['text']['align']) ? $options['text']['align'] : $this->defaultTextAlign;
498
        $fill = 0;
499
        $ln = 1;
500
        $reseth = TRUE;
501
        $stretch = 0;
502
        $ishtml = isset($options['render']['is_html']) ? $options['render']['is_html'] : 1;
503
        $stripHTML = !$ishtml;
504
        $autopadding = TRUE;
505
        $maxh = 0;
506
        $valign = 'T';
507
        $fitcell = FALSE;
508

    
509
        // Run eval before.
510
        if (VIEWS_PDF_PHP) {
511
          if (!empty($options['render']['bypass_eval_before']) && !empty($options['render']['eval_before'])) {
512
            eval($options['render']['eval_before']);
513
          }
514
          elseif (!empty($options['render']['eval_before']))  {
515
            $content = php_eval($options['render']['eval_before']);
516
          }
517
        }
518
        if ($options['render']['custom_layout']) {
519
          // Custom layout hook.
520
          $layout_data = array (
521
            'x'          => &$x,
522
            'y'          => &$y,
523
            'h'          => &$h,
524
            'w'          => &$w,
525
            'content'    => &$content,
526
            'key'        => &$key,
527
            'view'       => &$view,
528
            'this'       => &$this,
529
            'border'     => &$border,
530
            'color'      => &$textColor,
531
            'font'       => &$font_family,
532
            'font_style' => &$font_style,
533
            'font_size'  => &$font_size,
534

    
535
          );
536
          drupal_alter('views_pdf_custom_layout', $layout_data);
537
        }
538

    
539
        // Add css if there is a css file set and stripHTML is not active.
540
        if (!empty($css_file) && is_string($css_file) && !$stripHTML && $ishtml && !empty($content)) {
541
          $content = '<link type="text/css" rel="stylesheet" media="all" href="' . $css_file . '" />' . PHP_EOL . $content;
542
        }
543

    
544
        // Set Text Color.
545
        $this->SetTextColorArray($textColor);
546

    
547
        // Set font.
548
        $this->SetFont($font_family, implode('', $font_style), $font_size);
549

    
550
        // Save the last page before starting writing, this
551
        // is needed to dected if we write over a page. Then we need
552
        // to reset the y coordinate for the 'last_writing' position option.
553
        $this->lastWritingPage = $this->getPage();
554

    
555
        if ($stripHTML) {
556
          $content = strip_tags($content);
557
        }
558

    
559
        // Write the content of a field to the pdf file:
560
        if (!empty($content)) {
561
          $this->MultiCell($w, $h, $prefix . $content, $border, $align, $fill, $ln, $x, $y, $reseth, $stretch, $ishtml, $autopadding, $maxh, $valign, $fitcell);
562
        }
563
        else {
564
          $this->MultiCell($w, $h, $prefix, $border, $align, $fill, $ln, $x, $y, $reseth, $stretch, $ishtml, $autopadding, $maxh, $valign, $fitcell);
565
        }
566

    
567
        // Reset font to default.
568
        $this->SetFont($this->defaultFontFamily, implode('', $this->defaultFontStyle), $this->defaultFontSize);
569

    
570
        // Post render.
571
        if ($options['render']['custom_post']) {
572
          drupal_alter('views_pdf_custom_post', $view);
573
        }
574
        // Run eval after.
575
        if (VIEWS_PDF_PHP) {
576
          if (!empty($options['render']['bypass_eval_after']) && !empty($options['render']['eval_after'])) {
577
            eval($options['render']['eval_after']);
578
          }
579
          elseif (!empty($options['render']['eval_after'])) {
580
            $content = php_eval($options['render']['eval_after']);
581
          }
582
        }
583

    
584
        // Write Coordinates of element.
585
        $this->elements[$key] = array(
586
          'x' => $x,
587
          'y' => $y,
588
          'width' => empty($w) ? ($pageDim['wk'] - $this->rMargin-$x) : $w,
589
          'height' => $this->y - $y,
590
          'page' => $this->lastWritingPage,
591
        );
592

    
593
        $this->lastWritingElement = $key;
594
      }
595

    
596
  }
597

    
598
  /**
599
   * This method draws a table on the PDF.
600
   */
601
  public function drawTable(&$view, $options) {
602

    
603
    $rows = $view->result;
604
    $columns = $view->field;
605
    $pageDim = $this->getPageDimensions();
606

    
607
    // Set draw point to the indicated position:
608
    if (empty($options['position']['x'])) {
609
      $options['position']['x'] = 0;
610
    }
611

    
612
    if (empty($options['position']['y'])) {
613
      $options['position']['y'] = 0;
614
    }
615

    
616
    if (isset($options['position']['last_writing_position']) && $options['position']['last_writing_position']) {
617
      $y = $options['position']['y'] + $this->y;
618
      $x = $options['position']['x'] + $this->x;
619
    }
620
    else {
621
      $y = $options['position']['y'];
622
      $x = $options['position']['x'];
623
    }
624

    
625
    if (isset($options['position']['width']) && !empty($options['position']['width'])) {
626
      $width = $options['position']['width'];
627
    }
628
    else {
629
      $width = $pageDim['wk'] - $this->rMargin - $x;
630
    }
631

    
632
    $sumWidth = 0;
633
    $numberOfColumnsWithoutWidth = 0;
634

    
635
    // Set the definitiv width of a column
636
    foreach ($columns as $id => $columnName) {
637
      if (isset($options['info'][$id]['position']['width']) && !empty($options['info'][$id]['position']['width'])) {
638
        $sumWidth += $options['info'][$id]['position']['width'];
639
      }
640
      else {
641
        $numberOfColumnsWithoutWidth++;
642
      }
643
    }
644
    if ($numberOfColumnsWithoutWidth > 0) {
645
      $defaultColumnWidth = ($width - $sumWidth) / $numberOfColumnsWithoutWidth;
646
    }
647
    else {
648
      $defaultColumnWidth = 0;
649
    }
650

    
651
    // Print header:
652
    $rowX = $x;
653
    $page = $this->getPage();
654
    if ($page == 0) {
655
      $this->addPage();
656
      $page = $this->getPage();
657
    }
658

    
659
    if (!isset($options['position']['row_height']) || empty($options['position']['row_height'])) {
660
      $options['position']['row_height'] = 0;
661
    }
662

    
663
    foreach ($columns as $id => $column) {
664

    
665
      if (!empty($column->options['exclude'])) {
666
        continue;
667
      }
668

    
669
      if (!is_array($options['info'][$id])) {
670
        $options['info'][$id] = array();
671
      }
672

    
673
      $options['info'][$id] += array(
674
        'header_style' => array(),
675
        'body_style' => array(),
676
      );
677

    
678
      $options['info'][$id]['header_style'] += array(
679
        'position' => array(),
680
        'text' => array(),
681
        'render' => array(),
682
      );
683

    
684
      $options['info'][$id]['header_style']['position'] += array(
685
        'corner' => 'top_left',
686
        'x' => NULL,
687
        'y' => NULL,
688
        'object' => '',
689
        'width' => NULL,
690
        'height' => NULL,
691
      );
692

    
693
      $options['info'][$id]['header_style']['text'] += array(
694
        'font_family' => 'default',
695
        'font_style' => '',
696
      );
697

    
698
      $options['info'][$id]['header_style']['text'] += array(
699
        'eval_before' => '',
700
        'eval_after' => '',
701
      );
702

    
703
      $options['info'][$id]['body_style'] += array(
704
        'position' => array(),
705
        'text' => array(),
706
        'render' => array(),
707
      );
708

    
709
      $options['info'][$id]['body_style']['position'] += array(
710
        'corner' => 'top_left',
711
        'x' => NULL,
712
        'y' => NULL,
713
        'object' => '',
714
        'width' => NULL,
715
        'height' => NULL,
716
      );
717

    
718
      $options['info'][$id]['body_style']['text'] += array(
719
        'font_family' => 'default',
720
        'font_style' => '',
721
      );
722

    
723
      $options['info'][$id]['body_style']['text'] += array(
724
        'eval_before' => '',
725
        'eval_after' => '',
726
      );
727

    
728
      $headerOptions = $options['info'][$id]['header_style'];
729

    
730
      if (isset($options['info'][$id]['position']['width']) && !empty($options['info'][$id]['position']['width'])) {
731
        $headerOptions['position']['width'] = $options['info'][$id]['position']['width'];
732
      }
733
      else {
734
        $headerOptions['position']['width'] = $defaultColumnWidth;
735
      }
736
      $headerOptions['position']['object'] = 'last_position_without_reset';
737

    
738
      $this->SetY($y);
739
      $this->SetX($x);
740
      $this->setPage($page);
741

    
742
      $this->renderRow($x, $y, $column->options['label'], $headerOptions, $view, $id, FALSE);
743
      $x += $headerOptions['position']['width'];
744
    }
745

    
746
    $rowY = $this->y;
747

    
748
    if (!isset($options['position']['row_height']) || empty($options['position']['row_height'])) {
749
      $options['position']['row_height'] = 0;
750
    }
751

    
752
    foreach ($rows as $row) {
753
      $x = $rowX;
754

    
755
       // Get the page dimensions
756
      $pageDim = $this->getPageDimensions();
757

    
758
      if (($rowY + $this->bMargin + $options['position']['row_height']) > $pageDim['hk']) {
759
        $rowY = $this->tMargin;
760
        $this->addPage();
761
      }
762

    
763
      if ($this->lastWritingPage != $this->getPage()) {
764
        $rowY = $this->y; // $rowY - $pageDim['hk']
765
      }
766

    
767
      $y = $rowY;
768
      $page = $this->getPage();
769
      foreach ($columns as $id => $column) {
770

    
771
        if (!empty($column->options['exclude']) && is_object($view->field[$id])) {
772
          // Render the element, but dont print the output. This
773
          // is required to allow the use of tokens in other fields.
774
          $view->field[$id]->theme($row);
775
          continue;
776
        }
777

    
778
        $bodyOptions = $options['info'][$id]['body_style'];
779

    
780
        if (isset($options['info'][$id]['position']['width']) && !empty($options['info'][$id]['position']['width'])) {
781
          $bodyOptions['position']['width'] = $options['info'][$id]['position']['width'];
782
        }
783
        else {
784
          $bodyOptions['position']['width'] = $defaultColumnWidth;
785
        }
786
        $bodyOptions['position']['object'] = 'last_position';
787

    
788
        $this->setPage($page);
789
        $this->SetY($y);
790
        $this->SetX($x);
791

    
792
        $bodyOptions['position']['height'] = 0;
793

    
794
        $this->renderRow($x, $y, $row, $bodyOptions, $view, $id, FALSE);
795

    
796
        $x += $bodyOptions['position']['width'];
797

    
798
        // If the cell is writting over the row, we need to adjust the
799
        // row y position.
800
        if (($rowY + $options['position']['row_height']) < $this->y) {
801
          $rowY = $this->y - $options['position']['row_height'];
802
        }
803

    
804
      }
805

    
806
      $rowY += $options['position']['row_height'];
807

    
808
      $view->row_index++;
809
    }
810

    
811
    $this->SetY($rowY + $options['position']['row_height']);
812
  }
813

    
814
  /**
815
   * This method adds a existing PDF document to the current document. If
816
   * the file does not exists this method will return 0. In all other cases
817
   * it will returns the number of the added pages.
818
   *
819
   * @param $path string Path to the file
820
   * @return integer Number of added pages
821
   */
822
  public function addPdfDocument($path) {
823
    if (empty($path) || !file_exists($path)) {
824
      return 0;
825
    }
826

    
827
    $numberOfPages = $this->setSourceFile($path);
828
    for ($i = 1; $i <= $numberOfPages; $i++) {
829

    
830
      $dim = $this->getTemplateSize($i);
831
      $format[0] = $dim['w'];
832
      $format[1] = $dim['h'];
833

    
834
      if ($dim['w'] > $dim['h']) {
835
        $orientation = 'L';
836
      }
837
      else {
838
        $orientation = 'P';
839
      }
840
      $this->setPageFormat($format, $orientation);
841
      parent::addPage();
842

    
843
      // Ensure that all new content is printed to a new page
844
      $this->y = 0;
845

    
846
      $page = $this->importPage($i);
847
      $this->useTemplate($page, 0, 0);
848
      $this->addNewPageBeforeNextContent = TRUE;
849
    }
850

    
851
    return $numberOfPages;
852

    
853
  }
854

    
855
  /**
856
   * This method resets the page number. This is useful if you want to start
857
   * the numbering by zero.
858
   */
859
  public function resetRowPageNumber() {
860
    $this->rowContentPageNumber = 0;
861
  }
862

    
863
  /**
864
   * This method adds a new page to the PDF.
865
   */
866
  public function addPage($orientation = '', $format = '', $keepmargins = false, $tocpage = false, $path = NULL, $reset = FALSE, $numbering = 'main') {
867

    
868
    // Do not add any new page, if we are writing
869
    // in the footer or header.
870
    if ($this->InFooter) {
871
      return;
872
    }
873

    
874
    $this->mainContentPageNumber++;
875
    $this->rowContentPageNumber++;
876

    
877
    // Prevent a reset without any template
878
    if ($reset == TRUE && (empty($path) || !file_exists($path))) {
879
      parent::addPage();
880
      $this->setPageFormat($this->defaultFormat, $this->defaultOrientation);
881
      return;
882
    }
883

    
884
    $files = $this->defaultPageTemplateFiles;
885

    
886
    // Reset with new template
887
    if ($reset) {
888
      $files = array();
889
    }
890

    
891
    if ($path != NULL) {
892
      $files[] = array('path' => $path, 'numbering' => $numbering);
893
    }
894
    $format = FALSE;
895
    foreach ($files as $file) {
896
      if (!empty($file['path']) && file_exists($file['path'])) {
897
        $path = realpath($file['path']);
898

    
899
        $numberOfPages = $this->setSourceFile($path);
900
        if ($file['numbering'] == 'row')  {
901
          $index = min($this->rowContentPageNumber, $numberOfPages);
902
        }
903
        else {
904
          $index = min($this->mainContentPageNumber, $numberOfPages);
905
        }
906

    
907

    
908
        $page = $this->importPage($index);
909

    
910
        // ajust the page format (only for the first template)
911
        if ($format == FALSE) {
912

    
913
          $dim = $this->getTemplateSize($index);
914
          $format[0] = $dim['w'];
915
          $format[1] = $dim['h'];
916
          //$this->setPageFormat($format);
917
          if ($dim['w'] > $dim['h']) {
918
            $orientation = 'L';
919
          }
920
          else {
921
            $orientation = 'P';
922
          }
923
          $this->setPageFormat($format, $orientation);
924
          parent::addPage();
925
        }
926

    
927
        // Apply the template
928
        $this->useTemplate($page, 0, 0);
929
      }
930
    }
931

    
932
    // if all paths were empty, ensure that at least the page is added
933
    if ($format == FALSE) {
934
      parent::addPage();
935
      $this->setPageFormat($this->defaultFormat, $this->defaultOrientation);
936
    }
937

    
938
  }
939

    
940
  /**
941
   * Sets the current header and footer of the page.
942
   */
943
  public function setHeaderFooter($record, $options, $view) {
944
    //if ($this->getPage() > 0 && !isset($this->headerFooterData[$this->getPage()])) {
945
      $this->headerFooterData[$this->getPage()] = $record;
946
    //}
947
    $this->headerFooterOptions = $options;
948
    $this->view =& $view;
949
  }
950

    
951
  /**
952
   * Close the document. This is called automatically by
953
   * TCPDF::Output().
954
   */
955
  public function Close() {
956
    // Print the Header & Footer
957
    for ($page = 1; $page <= $this->getNumPages(); $page++) {
958
      $this->setPage($page);
959

    
960
      if (isset($this->headerFooterData[$page])) {
961
        $record = $this->headerFooterData[$page];
962
        if (is_array($this->headerFooterOptions['formats'])) {
963
          foreach ($this->headerFooterOptions['formats'] as $id => $options) {
964
            if ($options['position']['object'] == 'header_footer') {
965
              $fieldOptions = $options;
966
              $fieldOptions['position']['object'] = 'page';
967
              $this->InFooter = TRUE;
968

    
969
              // backup margins
970
              $ml = $this->lMargin;
971
              $mr = $this->rMargin;
972
              $mt = $this->tMargin;
973
              $this->SetMargins(0, 0, 0);
974

    
975
              $this->drawContent($record, $fieldOptions, $this->view, $id);
976
              $this->InFooter = FALSE;
977

    
978
              // restore margins
979
              $this->SetMargins($ml, $mt, $mr);
980
            }
981
          }
982
        }
983
      }
984
    }
985

    
986
    // call parent:
987
    parent::Close();
988

    
989
  }
990

    
991
  /**
992
   * This method returns a list of current uploaded files.
993
   */
994
  public static function getAvailableTemplates() {
995
    if (self::$templateList != NULL) {
996
      return self::$templateList;
997
    }
998

    
999
    $files_path = drupal_realpath('public://');
1000
    $template_dir = variable_get('views_pdf_template_path', 'views_pdf_templates');
1001
    $dir = $files_path . '/' . $template_dir;
1002
    $templatesFiles = file_scan_directory($dir, '/.pdf$/', array('nomask' => '/(\.\.?|CVS)$/'), 1);
1003

    
1004
    $templates = array();
1005

    
1006
    foreach ($templatesFiles as $file) {
1007
      $templates[$file->filename] = $file->name;
1008
    }
1009

    
1010
    self::$templateList = $templates;
1011

    
1012
    return $templates;
1013

    
1014
  }
1015

    
1016
  /**
1017
   * This method returns the path to a specific template.
1018
   */
1019
  public static function getTemplatePath($template, $row = NULL, $view = NULL) {
1020
    if (empty($template)) {
1021
      return '';
1022
    }
1023

    
1024
    if ($row != NULL && $view != NULL && !preg_match('/\.pdf/', $template)) {
1025
      return drupal_realpath($row->field_data_field_file_node_values[0]['uri']);
1026
    }
1027

    
1028
    $template_dir = variable_get('views_pdf_template_stream', 'public://views_pdf_templates');
1029
    return drupal_realpath($template_dir . '/' . $template);
1030
  }
1031

    
1032
  /**
1033
   * This method returns a list of available fonts.
1034
   */
1035
  public static function getAvailableFonts() {
1036
    if (self::$fontList != NULL) {
1037
      return self::$fontList;
1038
    }
1039

    
1040
    // Get all pdf files with the font list: K_PATH_FONTS
1041
    $fonts = file_scan_directory(K_PATH_FONTS, '/.php$/', array('nomask' => '/(\.\.?|CVS)$/', 'recurse' => FALSE), 1);
1042
    $cache = cache_get('views_pdf_cached_fonts');
1043

    
1044
    $cached_font_mapping = NULL;
1045

    
1046
    if (is_object($cache)) {
1047
      $cached_font_mapping = $cache->data;
1048
    }
1049

    
1050
    if (is_array($cached_font_mapping)) {
1051
      $font_mapping = array_merge(self::$defaultFontList, $cached_font_mapping);
1052
    }
1053
    else {
1054
      $font_mapping = self::$defaultFontList;
1055
    }
1056

    
1057
    foreach ($fonts as $font) {
1058
        $name = self::getFontNameByFileName($font->uri);
1059
        if (isset($name)) {
1060
          $font_mapping[$font->name] = $name;
1061
        }
1062
    }
1063

    
1064
    asort($font_mapping);
1065

    
1066
    cache_set('views_pdf_cached_fonts', $font_mapping);
1067

    
1068
    // Remove all fonts without name
1069
    foreach ($font_mapping as $key => $font) {
1070
      if (empty($font)) {
1071
        unset($font_mapping[$key]);
1072
      }
1073

    
1074
    }
1075

    
1076
    self::$fontList = $font_mapping;
1077

    
1078
    return $font_mapping;
1079
  }
1080

    
1081
  /**
1082
   * This method returns a cleaned up version of the font list.
1083
   */
1084
  public static function getAvailableFontsCleanList() {
1085
    if (self::$fontListClean != NULL) {
1086
      return self::$fontListClean;
1087
    }
1088

    
1089
    $clean = self::getAvailableFonts();
1090

    
1091
    foreach ($clean as $key => $font) {
1092

    
1093
      // Unset bold, italic, italic/bold fonts
1094
      unset($clean[ ($key . 'b') ]);
1095
      unset($clean[ ($key . 'bi') ]);
1096
      unset($clean[ ($key . 'i') ]);
1097

    
1098
    }
1099

    
1100
    self::$fontListClean = $clean;
1101

    
1102
    return $clean;
1103
  }
1104

    
1105
  /**
1106
   * This method returns a list of hyphenation patterns, that are
1107
   * available.
1108
   */
1109
  public static function getAvailableHyphenatePatterns() {
1110
    if (self::$hyphenatePatterns != NULL) {
1111
      return self::$hyphenatePatterns;
1112
    }
1113

    
1114
    self::$hyphenatePatterns = array();
1115

    
1116
    $files = file_scan_directory(views_pdf_get_library('tcpdf') . '/hyphenate_patterns', '/.tex$/', array('nomask' => '/(\.\.?|CVS)$/'), 1);
1117

    
1118
    foreach ($files as $file) {
1119
      self::$hyphenatePatterns[basename($file->uri)] = str_replace('hyph-', '', $file->name);
1120
    }
1121

    
1122

    
1123
    return self::$hyphenatePatterns;
1124
  }
1125

    
1126
  /**
1127
   * This method returns the name of a given font.
1128
   */
1129
  protected static function getFontNameByFileName($path) {
1130
    include $path;
1131
    if (isset($name)) {
1132
      return $name;
1133
    }
1134
    else {
1135
      return NULL;
1136
    }
1137
  }
1138
}