1: <?php
2: 3: 4: 5: 6: 7: 8: 9:
10:
11: namespace Kotchasan;
12:
13: 14: 15: 16: 17: 18: 19:
20: class Pdf extends \PDF\FPDF
21: {
22: 23: 24:
25: protected $B;
26: 27: 28:
29: protected $I;
30: 31: 32:
33: protected $U;
34: 35: 36:
37: protected $css;
38: 39: 40:
41: protected $cssClass;
42: 43: 44:
45: protected $fontSize;
46: 47: 48:
49: protected $lastBlock = true;
50: 51: 52:
53: protected $lineHeight = 5;
54: 55: 56:
57: protected $link = null;
58: 59: 60:
61: protected $unit;
62:
63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78:
79: public function Cell($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = false, $link = '', $tPadding = 0, $rPadding = 0, $bPadding = 0, $lPadding = 0)
80: {
81: $k = $this->k;
82: if ($this->y + $h > $this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak()) {
83:
84: $x = $this->x;
85: $ws = $this->ws;
86: if ($ws > 0) {
87: $this->ws = 0;
88: $this->_out('0 Tw');
89: }
90: $this->AddPage($this->CurOrientation, $this->CurPageSize, $this->CurRotation);
91: $this->x = $x;
92: if ($ws > 0) {
93: $this->ws = $ws;
94: $this->_out(sprintf('%.3F Tw', $ws * $k));
95: }
96: }
97: if ($w == 0) {
98: $w = $this->w - $this->rMargin - $this->x;
99: }
100: $s = '';
101: if ($fill || $border == 1) {
102: if ($fill) {
103: $op = ($border == 1) ? 'B' : 'f';
104: } else {
105: $op = 'S';
106: }
107: $s = sprintf('%.2F %.2F %.2F %.2F re %s ', $this->x * $k, ($this->h - $this->y + $tPadding) * $k, $w * $k, (-$h - $bPadding - $tPadding) * $k, $op);
108: }
109: if (is_string($border)) {
110: $x = $this->x;
111: $y = $this->y;
112: if (strpos($border, 'L') !== false) {
113: $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $x * $k, ($this->h - $y + $tPadding) * $k, $x * $k, ($this->h - ($y + $h) - $bPadding) * $k);
114: }
115: if (strpos($border, 'T') !== false) {
116: $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $x * $k, ($this->h - $y + $tPadding) * $k, ($x + $w) * $k, ($this->h - $y + $tPadding) * $k);
117: }
118: if (strpos($border, 'R') !== false) {
119: $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', ($x + $w) * $k, ($this->h - $y + $tPadding) * $k, ($x + $w) * $k, ($this->h - ($y + $h) - $bPadding) * $k);
120: }
121: if (strpos($border, 'B') !== false) {
122: $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $x * $k, ($this->h - ($y + $h) - $bPadding) * $k, ($x + $w) * $k, ($this->h - ($y + $h) - $bPadding) * $k);
123: }
124: }
125: if ($txt !== '') {
126: if (!isset($this->CurrentFont)) {
127: $this->Error('No font has been set');
128: }
129: if ($align == 'R') {
130: $dx = $w - $this->cMargin - $this->GetStringWidth($txt) - $rPadding;
131: } elseif ($align == 'C') {
132: $dx = ($w - $this->GetStringWidth($txt)) / 2;
133: } else {
134: $dx = $this->cMargin + $lPadding;
135: }
136: if ($this->ColorFlag) {
137: $s .= 'q '.$this->TextColor.' ';
138: }
139: $s .= sprintf('BT %.2F %.2F Td (%s) Tj ET', ($this->x + $dx) * $k, ($this->h - ($this->y + .5 * $h + .3 * $this->FontSize)) * $k, $this->_escape($txt));
140: if ($this->underline) {
141: $s .= ' '.$this->_dounderline($this->x + $dx, $this->y + .5 * $h + .3 * $this->FontSize, $txt);
142: }
143: if ($this->ColorFlag) {
144: $s .= ' Q';
145: }
146: if ($link) {
147: $this->Link($this->x + $dx, $this->y + .5 * $h - .5 * $this->FontSize, $this->GetStringWidth($txt), $this->FontSize, $link);
148: }
149: }
150: if ($s) {
151: $this->_out($s);
152: }
153: $this->lasth = $h;
154: if ($ln > 0) {
155:
156: $this->y += $h;
157: if ($ln == 1) {
158: $this->x = $this->lMargin;
159: }
160: } else {
161: $this->x += $w;
162: }
163: }
164:
165: 166: 167: 168: 169:
170: public function Ln($h = null)
171: {
172:
173: $this->x = $this->lMargin;
174: $this->y += ($h ? $h : $this->lineHeight);
175:
176: $this->lastBlock = true;
177: }
178:
179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192:
193: public function MultiCell($w, $h, $s, $border = 0, $align = 'J', $fill = false, $tPadding = 0, $rPadding = 0, $bPadding = 0, $lPadding = 0)
194: {
195: if (!isset($this->CurrentFont)) {
196: $this->Error('No font has been set');
197: }
198: if ($border == 1) {
199: $border = 'RLTB';
200: }
201: $startY = $this->y;
202: $this->y += $tPadding;
203: $cw = &$this->CurrentFont['cw'];
204: if ($w == 0) {
205: $cell_width = $this->w - $this->rMargin - $this->x;
206: $w = $cell_width - $lPadding - $rPadding;
207: } else {
208: $cell_width = $w;
209: }
210: $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
211: $nb = strlen($s);
212: if ($nb > 0 && $s[$nb - 1] == "\n") {
213: --$nb;
214: }
215: $sep = -1;
216: $i = 0;
217: $j = 0;
218: $l = 0;
219: $ns = 0;
220: $nl = 1;
221: while ($i < $nb) {
222:
223: $c = $s[$i];
224: if ($c == "\n") {
225:
226: if ($this->ws > 0) {
227: $this->ws = 0;
228: $this->_out('0 Tw');
229: }
230: if ($nl == 1) {
231:
232: $this->Cell($cell_width, $h, substr($s, $j, $i - $j), str_replace('B', '', $border), 2, $align, $fill, '', $tPadding, $rPadding, 0, $lPadding);
233: } else {
234: $this->Cell($cell_width, $h, substr($s, $j, $i - $j), str_replace(array('T', 'B'), '', $border), 2, $align, $fill, '', 0, $rPadding, 0, $lPadding);
235: }
236: ++$i;
237: $sep = -1;
238: $j = $i;
239: $l = 0;
240: $ns = 0;
241: ++$nl;
242: continue;
243: } elseif ($c == ' ') {
244: $sep = $i;
245: $ls = $l;
246: ++$ns;
247: }
248: $l += $cw[$c];
249: if ($l > $wmax) {
250:
251: if ($sep == -1) {
252: if ($i == $j) {
253: ++$i;
254: }
255: if ($this->ws > 0) {
256: $this->ws = 0;
257: $this->_out('0 Tw');
258: }
259: if ($nl == 1) {
260:
261: $this->Cell($cell_width, $h, substr($s, $j, $i - $j), str_replace('B', '', $border), 2, $align, $fill, '', $tPadding, $rPadding, 0, $lPadding);
262: } else {
263: $this->Cell($cell_width, $h, substr($s, $j, $i - $j), str_replace(array('T', 'B'), '', $border), 2, $align, $fill, '', 0, $rPadding, 0, $lPadding);
264: }
265: } else {
266: if ($align == 'J') {
267: $this->ws = ($ns > 1) ? ($wmax - $ls) / 1000 * $this->FontSize / ($ns - 1) : 0;
268: $this->_out(sprintf('%.3F Tw', $this->ws * $this->k));
269: }
270: if ($nl == 1) {
271:
272: $this->Cell($cell_width, $h, substr($s, $j, $sep - $j), str_replace('B', '', $border), 2, $align, $fill, '', $tPadding, $rPadding, 0, $lPadding);
273: } else {
274: $this->Cell($cell_width, $h, substr($s, $j, $sep - $j), str_replace(array('T', 'B'), '', $border), 2, $align, $fill, '', 0, $rPadding, 0, $lPadding);
275: }
276: $i = $sep + 1;
277: }
278: $sep = -1;
279: $j = $i;
280: $l = 0;
281: $ns = 0;
282: ++$nl;
283: } else {
284: ++$i;
285: }
286: }
287:
288: if ($this->ws > 0) {
289: $this->ws = 0;
290: $this->_out('0 Tw');
291: }
292: if ($nl == 1) {
293:
294: $this->Cell($cell_width, $h, substr($s, $j, $i - $j), $border, 2, $align, $fill, '', $tPadding, $rPadding, $bPadding, $lPadding);
295: } else {
296:
297: $this->Cell($cell_width, $h, substr($s, $j, $i - $j), str_replace('T', '', $border), 2, $align, $fill, '', 0, $rPadding, $bPadding, $lPadding);
298: }
299: $this->y += $bPadding;
300: $this->x = $this->lMargin;
301: }
302:
303: 304: 305: 306: 307: 308:
309: public function SetCssClass($className, $attributes)
310: {
311: foreach ($attributes as $key => $value) {
312: $this->cssClass[strtoupper($className)][strtoupper($key)] = $value;
313: }
314: }
315:
316: 317: 318: 319: 320: 321:
322: public function SetStyles($tag, $attributes)
323: {
324: foreach ($attributes as $key => $value) {
325: $this->css[strtoupper($tag)][strtoupper($key)] = $value;
326: }
327: }
328:
329: 330: 331: 332: 333: 334: 335:
336: public function WriteHTML($html, $charset = 'cp874')
337: {
338:
339: $dom = new DOMParser($html, $charset);
340:
341: foreach ($dom->nodes() as $node) {
342: $this->render($node);
343: }
344: }
345:
346: 347: 348: 349: 350: 351: 352: 353:
354: public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4', $fontSize = 10)
355: {
356:
357: parent::__construct($orientation, $unit, $size);
358:
359: $this->B = 0;
360: $this->I = 0;
361: $this->U = 0;
362: $this->unit = $unit;
363: $this->fontSize = $fontSize;
364:
365: $this->AddFont('loma', '', 'Loma.php');
366: $this->AddFont('loma', 'B', 'Loma-Bold.php');
367: $this->AddFont('loma', 'I', 'Loma-Oblique.php');
368: $this->AddFont('loma', 'BI', 'Loma-BoldOblique.php');
369: $this->AddFont('angsana', '', 'angsa.php');
370: $this->AddFont('angsana', 'B', 'angsab.php');
371: $this->AddFont('angsana', 'I', 'angsai.php');
372: $this->AddFont('angsana', 'BI', 'angsaz.php');
373:
374: $this->SetFont('loma', '', $this->fontSize);
375:
376: $this->css = array(
377: 'H1' => array(
378: 'SIZE' => $this->fontSize + 10,
379: 'LINE-HEIGHT' => $this->lineHeight + 2.5,
380: ),
381: 'H2' => array(
382: 'SIZE' => $this->fontSize + 8,
383: 'LINE-HEIGHT' => $this->lineHeight + 2,
384: ),
385: 'H3' => array(
386: 'SIZE' => $this->fontSize + 6,
387: 'LINE-HEIGHT' => $this->lineHeight + 1.5,
388: ),
389: 'H4' => array(
390: 'SIZE' => $this->fontSize + 4,
391: 'LINE-HEIGHT' => $this->lineHeight + 1,
392: ),
393: 'H5' => array(
394: 'SIZE' => $this->fontSize + 2,
395: 'LINE-HEIGHT' => $this->lineHeight + 0.5,
396: ),
397: 'EM' => array(
398: 'COLOR' => '#FF5722',
399: ),
400: 'I' => array(
401: 'FONT-STYLE' => 'ITALIC',
402: ),
403: 'B' => array(
404: 'FONT-WEIGHT' => 'BOLD',
405: ),
406: 'STRONG' => array(
407: 'FONT-WEIGHT' => 'BOLD',
408: ),
409: 'U' => array(
410: 'TEXT-DECORATION' => 'UNDERLINE',
411: ),
412: 'A' => array(
413: 'TEXT-DECORATION' => 'UNDERLINE',
414: ),
415: 'BLOCKQUOTE' => array(
416: 'BORDER-COLOR' => '#DDDDDD',
417: 'BACKGROUND-COLOR' => '#F9F9F9',
418: 'COLOR' => '#666666',
419: 'SIZE' => $this->fontSize - 1,
420: 'PADDING' => 2,
421: ),
422: 'CODE' => array(
423: 'BORDER-COLOR' => '#DDDDDD',
424: 'BACKGROUND-COLOR' => '#F9F9F9',
425: 'COLOR' => '#666666',
426: 'SIZE' => $this->fontSize + 4,
427: 'PADDING' => 2,
428: 'DISPLAY' => 'BLOCK',
429: 'FONT-FAMILY' => 'angsana',
430: 'FONT-STYLE' => 'ITALIC',
431: ),
432: 'TABLE' => array(
433: 'BORDER-COLOR' => '#DDDDDD',
434: ),
435: 'TH' => array(
436: 'TEXT-ALIGN' => 'CENTER',
437: 'BACKGROUND-COLOR' => '#EEEEEE',
438: ),
439: 'TD' => array(
440: 'COLOR' => '#333333',
441: ),
442: );
443:
444: $this->cssClass = array(
445: 'COMMENT' => array(
446: 'SIZE' => $this->fontSize - 1,
447: 'COLOR' => '#259B24',
448: ),
449: 'CENTER' => array(
450: 'TEXT-ALIGN' => 'CENTER',
451: ),
452: 'LEFT' => array(
453: 'TEXT-ALIGN' => 'LEFT',
454: ),
455: 'RIGHT' => array(
456: 'TEXT-ALIGN' => 'RIGHT',
457: ),
458: 'BG2' => array(
459: 'BACKGROUND-COLOR' => '#F9F9F9',
460: ),
461: 'FULLWIDTH' => array(
462: 'WIDTH' => '100%',
463: ),
464: );
465: }
466:
467: 468: 469: 470: 471: 472:
473: protected function CheckPageBreak($h)
474: {
475: if ($this->GetY() + $h > $this->PageBreakTrigger) {
476: $this->AddPage($this->CurOrientation);
477: }
478: }
479:
480: 481: 482: 483: 484: 485: 486: 487:
488: protected function NbLines($w, $s)
489: {
490: $cw = &$this->CurrentFont['cw'];
491: if ($w == 0) {
492: $w = $this->w - $this->rMargin - $this->x;
493: }
494: $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
495: $nb = strlen($s);
496: if ($nb > 0 && $s[$nb - 1] == "\n") {
497: --$nb;
498: }
499: $sep = -1;
500: $i = 0;
501: $j = 0;
502: $l = 0;
503: $nl = 1;
504: while ($i < $nb) {
505: $c = $s[$i];
506: if ($c == "\n") {
507: ++$i;
508: $sep = -1;
509: $j = $i;
510: $l = 0;
511: ++$nl;
512: continue;
513: }
514: if ($c == ' ') {
515: $sep = $i;
516: }
517: $l += $cw[$c];
518: if ($l > $wmax) {
519: if ($sep == -1) {
520: if ($i == $j) {
521: ++$i;
522: }
523: } else {
524: $i = $sep + 1;
525: }
526: $sep = -1;
527: $j = $i;
528: $l = 0;
529: ++$nl;
530: } else {
531: ++$i;
532: }
533: }
534:
535: return $nl;
536: }
537:
538: 539: 540: 541: 542: 543:
544: protected function SetStyle($style, $enable)
545: {
546: $this->{$style} += ($enable ? 1 : -1);
547: $font_style = '';
548: foreach (array('B', 'I', 'U') as $s) {
549: if ($this->{$s} > 0) {
550: $font_style .= $s;
551: }
552: }
553: $this->SetFont('', $font_style);
554: }
555:
556: 557: 558: 559: 560:
561: protected function applyCSS($node)
562: {
563:
564: if (!empty($node->attributes['FONT-FAMILY'])) {
565: $node->FontFamily = $this->FontFamily;
566: $this->SetFont($node->attributes['FONT-FAMILY']);
567: }
568:
569: if (!empty($node->attributes['COLOR'])) {
570: if (preg_match('/([0-9\.]+)\s(([0-9\.]+)\s([0-9\.]+)\sr)?g/', $this->TextColor, $match)) {
571: $node->TextColor = array(
572: 'r' => $match[1],
573: 'g' => isset($match[3]) ? $match[3] : null,
574: 'b' => isset($match[4]) ? $match[4] : null,
575: );
576: }
577: list($r, $g, $b) = $this->colorToRGb($node->attributes['COLOR']);
578: $this->SetTextColor($r, $g, $b);
579: }
580:
581: if (!empty($node->attributes['BACKGROUND-COLOR'])) {
582: if (preg_match('/([0-9\.]+)\s(([0-9\.]+)\s([0-9\.]+)\sr)?g/', $this->FillColor, $match)) {
583: $node->FillColor = array(
584: 'r' => $match[1],
585: 'g' => isset($match[3]) ? $match[3] : null,
586: 'b' => isset($match[4]) ? $match[4] : null,
587: );
588: }
589: list($r, $g, $b) = $this->colorToRGb($node->attributes['BACKGROUND-COLOR']);
590: $this->SetFillColor($r, $g, $b);
591: }
592:
593: if (!empty($node->attributes['BORDER-COLOR'])) {
594: if (preg_match('/([0-9\.]+)\s(([0-9\.]+)\s([0-9\.]+)\sR)?G/', $this->DrawColor, $match)) {
595: $node->DrawColor = array(
596: 'r' => $match[1],
597: 'g' => isset($match[3]) ? $match[3] : null,
598: 'b' => isset($match[4]) ? $match[4] : null,
599: );
600: }
601: list($r, $g, $b) = $this->colorToRGb($node->attributes['BORDER-COLOR']);
602: $this->SetDrawColor($r, $g, $b);
603: }
604:
605: if (!empty($node->attributes['FONT-WEIGHT'])) {
606: $this->SetStyle('B', $node->attributes['FONT-WEIGHT'] == 'BOLD');
607: }
608:
609: if (!empty($node->attributes['FONT-STYLE'])) {
610: $this->SetStyle('I', $node->attributes['FONT-STYLE'] == 'ITALIC');
611: }
612:
613: if (!empty($node->attributes['TEXT-DECORATION'])) {
614: $this->SetStyle('U', $node->attributes['TEXT-DECORATION'] == 'UNDERLINE');
615: }
616:
617: if (!empty($node->attributes['SIZE'])) {
618: $node->FontSizePt = $this->FontSizePt;
619: $this->SetFontSize($node->attributes['SIZE']);
620: }
621: }
622:
623: 624: 625: 626: 627: 628: 629:
630: protected function calculateColumnsWidth($table)
631: {
632:
633: $cw = $this->w - $this->lMargin - $this->rMargin;
634: if (!empty($table->attributes['WIDTH'])) {
635:
636: $table_width = $this->calculateSize($table->attributes['WIDTH'], $cw);
637: }
638: $columnSizes = array();
639: foreach ($table->childNodes as $child) {
640: foreach ($child->childNodes as $tr) {
641: foreach ($tr->childNodes as $col => $td) {
642:
643: $td->nodeValue = $td->nodeText();
644:
645: $td->textWidth = $this->GetStringWidth($td->nodeValue);
646:
647: unset($td->childNodes);
648:
649: $length = isset($table_width) && !empty($td->attributes['WIDTH']) ? $this->calculateSize($td->attributes['WIDTH'], $table_width) : $td->textWidth;
650: $columnSizes[$col]['max'] = !isset($columnSizes[$col]['max']) ? $length : ($columnSizes[$col]['max'] < $length ? $length : $columnSizes[$col]['max']);
651: $columnSizes[$col]['avg'] = !isset($columnSizes[$col]['avg']) ? $length : $columnSizes[$col]['avg'] + $length;
652: $columnSizes[$col]['raw'][] = $length;
653: }
654: }
655: }
656: $columnSizes = array_map(function ($columnSize) {
657: $columnSize['avg'] = $columnSize['avg'] / sizeof($columnSize['raw']);
658:
659: return $columnSize;
660: }, $columnSizes);
661: foreach ($columnSizes as $key => $columnSize) {
662: $colMaxSize = $columnSize['max'];
663: $colAvgSize = $columnSize['avg'];
664: $stdDeviation = $this->sd($columnSize['raw']);
665: $coefficientVariation = $stdDeviation / $colAvgSize;
666: $columnSizes[$key]['cv'] = $coefficientVariation;
667: $columnSizes[$key]['stdd'] = $stdDeviation;
668: $columnSizes[$key]['stdd/max'] = $stdDeviation / $colMaxSize;
669: if (($columnSizes[$key]['stdd/max'] < 0.3 || $coefficientVariation == 1) && ($coefficientVariation == 0 || ($coefficientVariation > 0.6 && $coefficientVariation < 1.5))) {
670: $columnSizes[$key]['calc'] = $colAvgSize;
671: } else {
672: if ($coefficientVariation > 1 && $columnSizes[$key]['stdd'] > 4.5 && $columnSizes[$key]['stdd/max'] > 0.2) {
673: $tmp = ($colMaxSize - $colAvgSize) / 2;
674: } else {
675: $tmp = 0;
676: }
677: $columnSizes[$key]['calc'] = $colAvgSize + ($colMaxSize / $colAvgSize) * 2 / abs(1 - $coefficientVariation);
678: $columnSizes[$key]['calc'] = $columnSizes[$key]['calc'] > $colMaxSize ? $colMaxSize - $tmp : $columnSizes[$key]['calc'];
679: }
680: }
681: $totalCalculatedSize = 0;
682: foreach ($columnSizes as $columnSize) {
683: $totalCalculatedSize += $columnSize['calc'];
684: }
685: $result = array();
686: foreach ($columnSizes as $key => $columnSize) {
687: if (empty($table_width)) {
688: $result[$key] = 100 / ($totalCalculatedSize / $columnSize['calc']);
689: } else {
690: $result[$key] = ($columnSize['calc'] * $table_width) / $totalCalculatedSize;
691: }
692: }
693:
694: return $result;
695: }
696:
697: 698: 699: 700: 701: 702: 703: 704:
705: protected function calculateSize($size, $max_size)
706: {
707: if (preg_match('/^([0-9]+)(px|pt|mm|cm|in|\%)?$/', strtolower($size), $match)) {
708: if ($match[2] == '%') {
709: return ($max_size * (int) $match[1]) / 100;
710: } else {
711: return (int) $match[1];
712: }
713: }
714:
715: return (int) $size;
716: }
717:
718: 719: 720: 721: 722: 723: 724: 725:
726: protected function colorToRGb($color)
727: {
728: return array(
729: hexdec(substr($color, 1, 2)),
730: hexdec(substr($color, 3, 2)),
731: hexdec(substr($color, 5, 2)),
732: );
733: }
734:
735: 736: 737: 738: 739:
740: protected function drawHr($node)
741: {
742:
743: $ln = 2;
744: if (!$this->lastBlock) {
745: if ($node->attributes['DISPLAY'] !== 'INLINE') {
746: $ln = 7;
747: } elseif ($node->previousSibling && $node->previousSibling->attributes['DISPLAY'] !== 'INLINE') {
748: $ln = 7;
749: }
750: }
751: $this->Ln($ln);
752:
753: $x = $this->GetX();
754: $y = $this->GetY();
755:
756: $cw = $this->w - $this->lMargin - $this->rMargin;
757: if (empty($node->attributes['WIDTH'])) {
758:
759: $w = $cw;
760: } else {
761:
762: $w = $this->calculateSize($node->attributes['WIDTH'], $cw);
763: if (!empty($node->attributes['ALIGN']) && $cw > $w) {
764: switch (strtoupper($node->attributes['ALIGN'])) {
765: case 'CENTER':
766: $x = ($cw - $w) / 2;
767: break;
768: case 'RIGHT':
769: $x = $cw - $w;
770: break;
771: }
772: }
773: }
774: if (!empty($node->attributes['COLOR'])) {
775: $node->DrawColor = $this->DrawColor;
776: list($r, $g, $b) = $this->colorToRGb($node->attributes['COLOR']);
777: $this->SetDrawColor($r, $g, $b);
778: }
779: $lineWidth = $this->LineWidth;
780: $this->SetLineWidth(0.4);
781: $this->Line($x, $y, $x + $w, $y);
782: $this->SetLineWidth($lineWidth);
783: if (!empty($node->attributes['COLOR'])) {
784: $this->DrawColor = $node->DrawColor;
785: }
786:
787: $this->Ln(2);
788: }
789:
790: 791: 792: 793: 794:
795: protected function drawImg($node)
796: {
797: if (isset($node->attributes['SRC']) && file_exists($node->attributes['SRC'])) {
798: list($left, $top, $width, $height) = $this->resizeImage($node);
799: if ($node->parentNode->nodeName == 'FIGURE') {
800: $this->Image($node->attributes['SRC'], $left, $top, $width, $height);
801: $this->lastBlock = true;
802: } else {
803: if ($node->attributes['DISPLAY'] == 'INLINE' && $node->previousSibling && $node->previousSibling->attributes['DISPLAY'] !== 'INLINE') {
804:
805: $x = $this->lMargin;
806: $y = $this->y + $this->lineHeight;
807: } else {
808:
809: $x = $this->GetX();
810: $y = $this->GetY();
811: }
812: $this->Image($node->attributes['SRC'], $x, $y);
813: $this->x = $x + $width;
814: $this->y = $y;
815: $this->lastBlock = false;
816: }
817: }
818: }
819:
820: 821: 822: 823: 824:
825: protected function drawTable($table)
826: {
827: if (!$this->lastBlock) {
828: $this->Ln();
829: }
830:
831: $columnSizes = $this->calculateColumnsWidth($table);
832:
833: $this->applyCSS($table);
834:
835: $lineHeight = $this->lineHeight + 2;
836:
837: foreach ($table->childNodes as $table_group) {
838: foreach ($table_group->childNodes as $tr) {
839:
840: $this->loadStyle($tr);
841:
842: $h = 0;
843: foreach ($tr->childNodes as $col => $td) {
844:
845: foreach ($tr->attributes as $key => $value) {
846: $td->attributes[$key] = $value;
847: }
848:
849: $this->loadStyle($td);
850:
851: $h = max($h, $this->NbLines($columnSizes[$col], $td->nodeValue));
852: }
853: $h = $h * $lineHeight;
854:
855: $this->CheckPageBreak($h);
856:
857: $y = $this->y;
858: foreach ($tr->childNodes as $col => $td) {
859:
860: $this->applyCSS($td);
861: $align = '';
862: if (!empty($td->attributes['TEXT-ALIGN'])) {
863: $align = $td->attributes['TEXT-ALIGN'][0];
864: }
865:
866: $x = $this->x;
867:
868: $this->Cell($columnSizes[$col], $h, '', 1, 0, '', !empty($td->attributes['BACKGROUND-COLOR']));
869:
870: $this->x = $x;
871: $this->y = $y;
872:
873: $this->MultiCell($columnSizes[$col], $lineHeight, $td->nodeValue, 0, $align);
874:
875: $this->x = $x + $columnSizes[$col];
876: $this->y = $y;
877:
878: $this->restoredCSS($td);
879: }
880: $this->SetXY($this->lMargin, $y + $h);
881: }
882: }
883:
884: $this->restoredCSS($table);
885:
886: $this->lastBlock = true;
887: }
888:
889: 890: 891: 892: 893:
894: protected function loadStyle($node)
895: {
896:
897: $node->attributes['DISPLAY'] = $node->isInlineElement() ? 'INLINE' : 'BLOCK';
898:
899: if (isset($this->css[$node->nodeName])) {
900: foreach ($this->css[$node->nodeName] as $key => $value) {
901: $node->attributes[$key] = $value;
902: }
903: }
904:
905: if (!empty($node->attributes['STYLE'])) {
906: foreach (explode(';', strtoupper($node->attributes['STYLE'])) as $style) {
907: if (preg_match('/^([A-Z\-]+)[\s]{0,}\:[\s]{0,}([A-Z0-9\-]+).*?/', trim($style), $match)) {
908: $node->attributes[$match[1]] = $match[2];
909: }
910: }
911: unset($node->attributes['STYLE']);
912: }
913:
914: if (isset($node->attributes['CLASS'])) {
915: foreach (explode(' ', $node->attributes['CLASS']) as $class) {
916: $class = strtoupper($class);
917: if (isset($this->cssClass[$class])) {
918: foreach ($this->cssClass[$class] as $key => $value) {
919: if (!isset($node->attributes[$key])) {
920: $node->attributes[$key] = $value;
921: }
922: }
923: }
924: }
925: unset($node->attributes['CLASS']);
926: }
927:
928: if (!empty($node->attributes['PADDING'])) {
929: $value = (int) $node->attributes['PADDING'];
930: if (!isset($node->attributes['PADDING-LEFT'])) {
931: $node->attributes['PADDING-LEFT'] = $value;
932: }
933: if (!isset($node->attributes['PADDING-TOP'])) {
934: $node->attributes['PADDING-TOP'] = $value;
935: }
936: if (!isset($node->attributes['PADDING-RIGHT'])) {
937: $node->attributes['PADDING-RIGHT'] = $value;
938: }
939: if (!isset($node->attributes['PADDING-BOTTOM'])) {
940: $node->attributes['PADDING-BOTTOM'] = $value;
941: }
942: unset($node->attributes['PADDING']);
943: }
944: }
945:
946: 947: 948: 949: 950:
951: protected function render($node)
952: {
953: if ($node->nodeName == '') {
954:
955: $node->attributes['DISPLAY'] = 'INLINE';
956: $lineHeight = empty($node->parentNode->attributes['LINE-HEIGHT']) ? $this->lineHeight : $node->parentNode->attributes['LINE-HEIGHT'];
957: if ($node->parentNode && $node->parentNode->attributes['DISPLAY'] !== 'INLINE' && sizeof($node->parentNode->childNodes) == 1) {
958:
959: $align = empty($node->parentNode->attributes['TEXT-ALIGN']) ? '' : $node->parentNode->attributes['TEXT-ALIGN'][0];
960: $border = empty($node->parentNode->attributes['BORDER-COLOR']) ? 0 : 1;
961: $fill = empty($node->parentNode->attributes['BACKGROUND-COLOR']) ? false : true;
962: $tPadding = empty($node->parentNode->attributes['PADDING-TOP']) ? 0 : $node->parentNode->attributes['PADDING-TOP'];
963: $rPadding = empty($node->parentNode->attributes['PADDING-RIGHT']) ? 0 : $node->parentNode->attributes['PADDING-RIGHT'];
964: $bPadding = empty($node->parentNode->attributes['PADDING-BOTTOM']) ? 0 : $node->parentNode->attributes['PADDING-BOTTOM'];
965: $lPadding = empty($node->parentNode->attributes['PADDING-LEFT']) ? 0 : $node->parentNode->attributes['PADDING-LEFT'];
966: $this->MultiCell(0, $lineHeight, $node->unentities($node->nodeValue), $border, $align, $fill, $tPadding, $rPadding, $bPadding, $lPadding);
967: $this->lastBlock = true;
968: } else {
969:
970: if ($this->link) {
971:
972: $this->Write($lineHeight, $node->unentities($node->nodeValue), $this->link);
973: } else {
974:
975: $this->Write($lineHeight, $node->unentities($node->nodeValue));
976: }
977: $this->lastBlock = false;
978: }
979: } else {
980:
981: $this->loadStyle($node);
982:
983: if ($node->nodeName == 'BR') {
984:
985: $this->Ln();
986: } elseif ($node->nodeName == 'IMG') {
987:
988: $this->drawImg($node);
989: } elseif ($node->nodeName == 'HR') {
990:
991: $this->drawHr($node);
992: } elseif ($node->nodeName == 'TABLE') {
993:
994: $this->drawTable($node);
995: } else {
996:
997: if (!$this->lastBlock) {
998: if ($node->attributes['DISPLAY'] !== 'INLINE') {
999: $this->Ln();
1000: } elseif ($node->previousSibling && $node->previousSibling->attributes['DISPLAY'] !== 'INLINE') {
1001: $this->Ln();
1002: }
1003: }
1004:
1005: if ($node->nodeName == 'A' && !empty($node->attributes['HREF'])) {
1006: $this->link = $node->attributes['HREF'];
1007: }
1008:
1009: $this->applyCSS($node);
1010:
1011: foreach ($node->childNodes as $child) {
1012: $this->render($child);
1013: }
1014:
1015: $this->restoredCSS($node);
1016: }
1017: }
1018: }
1019:
1020: 1021: 1022: 1023: 1024: 1025: 1026: 1027: 1028: 1029:
1030: protected function resizeImage($node)
1031: {
1032: list($width, $height) = getimagesize($node->attributes['SRC']);
1033: if ($width < $this->wPt && $height < $this->hPt) {
1034: $k = 72 / 96 / $this->k;
1035: $l = null;
1036: if (isset($node->parentNode->attributes['TEXT-ALIGN'])) {
1037: switch ($node->parentNode->attributes['TEXT-ALIGN']) {
1038: case 'CENTER':
1039: $l = ($this->w - ($width * $k)) / 2;
1040: break;
1041: case 'RIGHT':
1042: $l = ($this->w - ($width * $k));
1043: break;
1044: }
1045: }
1046:
1047: return array($l, null, $width * $k, $height * $k);
1048: } else {
1049: $ws = $this->wPt / $width;
1050: $hs = $this->hPt / $height;
1051: $scale = min($ws, $hs);
1052: if ($this->unit == 'pt') {
1053: $k = 1;
1054: } elseif ($this->unit == 'mm') {
1055: $k = 25.4 / 72;
1056: } elseif ($this->unit == 'cm') {
1057: $k = 2.54 / 72;
1058: } elseif ($this->unit == 'in') {
1059: $k = 1 / 72;
1060: }
1061:
1062: return array(null, null, ((($scale * $width) - 56.7) * $k), ((($scale * $height) - 56.7) * $k));
1063: }
1064: }
1065:
1066: 1067: 1068: 1069: 1070:
1071: protected function restoredCSS($node)
1072: {
1073:
1074: if (!empty($node->attributes['FONT-FAMILY'])) {
1075: $this->SetFont($node->FontFamily);
1076: }
1077:
1078: if (!empty($node->attributes['BORDER-COLOR']) && isset($node->DrawColor)) {
1079: $this->SetDrawColor($node->DrawColor['r'], $node->DrawColor['g'], $node->DrawColor['b']);
1080: }
1081:
1082: if (!empty($node->attributes['BACKGROUND-COLOR']) && isset($node->FillColor)) {
1083: $this->SetFillColor($node->FillColor['r'], $node->FillColor['g'], $node->FillColor['b']);
1084: }
1085:
1086: if (!empty($node->attributes['COLOR']) && isset($node->TextColor)) {
1087: $this->SetTextColor($node->TextColor['r'], $node->TextColor['g'], $node->TextColor['b']);
1088: }
1089:
1090: if (!empty($node->attributes['FONT-WEIGHT'])) {
1091: $this->SetStyle('B', $node->attributes['FONT-WEIGHT'] != 'BOLD');
1092: }
1093:
1094: if (!empty($node->attributes['FONT-STYLE'])) {
1095: $this->SetStyle('I', $node->attributes['FONT-STYLE'] != 'ITALIC');
1096: }
1097:
1098: if (!empty($node->attributes['TEXT-DECORATION'])) {
1099: $this->SetStyle('U', $node->attributes['TEXT-DECORATION'] != 'UNDERLINE');
1100: }
1101:
1102: if (!empty($node->attributes['SIZE'])) {
1103: $this->SetFontSize($node->FontSizePt);
1104: }
1105: }
1106:
1107: 1108: 1109: 1110: 1111: 1112: 1113:
1114: protected function sd($array)
1115: {
1116: if (sizeof($array) == 1) {
1117: return 1.0;
1118: }
1119: $sd_square = function ($x, $mean) {
1120: return pow($x - $mean, 2);
1121: };
1122:
1123: return sqrt(array_sum(array_map($sd_square, $array, array_fill(0, sizeof($array), (array_sum($array) / sizeof($array))))) / (sizeof($array) - 1));
1124: }
1125: }
1126: