1<?php 2 3namespace Mpdf\Tag; 4 5use Mpdf\Mpdf; 6 7class Meter extends InlineTag 8{ 9 10 public function open($attr, &$ahtml, &$ihtml) 11 { 12 $tag = $this->getTagName(); 13 $this->mpdf->inMeter = true; 14 15 $max = 1; 16 if (!empty($attr['MAX'])) { 17 $max = $attr['MAX']; 18 } 19 20 $min = 0; 21 if (!empty($attr['MIN']) && $tag === 'METER') { 22 $min = $attr['MIN']; 23 } 24 25 if ($max < $min) { 26 $max = $min; 27 } 28 29 $value = ''; 30 if (isset($attr['VALUE']) && ($attr['VALUE'] || $attr['VALUE'] === '0')) { 31 $value = $attr['VALUE']; 32 if ($value < $min) { 33 $value = $min; 34 } elseif ($value > $max) { 35 $value = $max; 36 } 37 } 38 39 $low = $min; 40 if (!empty($attr['LOW'])) { 41 $low = $attr['LOW']; 42 } 43 if ($low < $min) { 44 $low = $min; 45 } elseif ($low > $max) { 46 $low = $max; 47 } 48 $high = $max; 49 if (!empty($attr['HIGH'])) { 50 $high = $attr['HIGH']; 51 } 52 if ($high < $low) { 53 $high = $low; 54 } elseif ($high > $max) { 55 $high = $max; 56 } 57 if (!empty($attr['OPTIMUM'])) { 58 $optimum = $attr['OPTIMUM']; 59 } else { 60 $optimum = $min + (($max - $min) / 2); 61 } 62 if ($optimum < $min) { 63 $optimum = $min; 64 } elseif ($optimum > $max) { 65 $optimum = $max; 66 } 67 $type = ''; 68 if (!empty($attr['TYPE'])) { 69 $type = $attr['TYPE']; 70 } 71 $objattr = []; 72 $objattr['margin_top'] = 0; 73 $objattr['margin_bottom'] = 0; 74 $objattr['margin_left'] = 0; 75 $objattr['margin_right'] = 0; 76 $objattr['padding_top'] = 0; 77 $objattr['padding_bottom'] = 0; 78 $objattr['padding_left'] = 0; 79 $objattr['padding_right'] = 0; 80 $objattr['width'] = 0; 81 $objattr['height'] = 0; 82 $objattr['border_top']['w'] = 0; 83 $objattr['border_bottom']['w'] = 0; 84 $objattr['border_left']['w'] = 0; 85 $objattr['border_right']['w'] = 0; 86 87 $properties = $this->cssManager->MergeCSS('INLINE', $tag, $attr); 88 if (isset($properties ['DISPLAY']) && strtolower($properties ['DISPLAY']) === 'none') { 89 return; 90 } 91 $objattr['visibility'] = 'visible'; 92 if (isset($properties['VISIBILITY'])) { 93 $v = strtolower($properties['VISIBILITY']); 94 if (($v === 'hidden' || $v === 'printonly' || $v === 'screenonly') && $this->mpdf->visibility === 'visible') { 95 $objattr['visibility'] = $v; 96 } 97 } 98 99 if (isset($properties['MARGIN-TOP'])) { 100 $objattr['margin_top'] = $this->sizeConverter->convert( 101 $properties['MARGIN-TOP'], 102 $this->mpdf->blk[$this->mpdf->blklvl]['inner_width'], 103 $this->mpdf->FontSize, 104 false 105 ); 106 } 107 if (isset($properties['MARGIN-BOTTOM'])) { 108 $objattr['margin_bottom'] = $this->sizeConverter->convert( 109 $properties['MARGIN-BOTTOM'], 110 $this->mpdf->blk[$this->mpdf->blklvl]['inner_width'], 111 $this->mpdf->FontSize, 112 false 113 ); 114 } 115 if (isset($properties['MARGIN-LEFT'])) { 116 $objattr['margin_left'] = $this->sizeConverter->convert( 117 $properties['MARGIN-LEFT'], 118 $this->mpdf->blk[$this->mpdf->blklvl]['inner_width'], 119 $this->mpdf->FontSize, 120 false 121 ); 122 } 123 if (isset($properties['MARGIN-RIGHT'])) { 124 $objattr['margin_right'] = $this->sizeConverter->convert( 125 $properties['MARGIN-RIGHT'], 126 $this->mpdf->blk[$this->mpdf->blklvl]['inner_width'], 127 $this->mpdf->FontSize, 128 false 129 ); 130 } 131 132 if (isset($properties['PADDING-TOP'])) { 133 $objattr['padding_top'] = $this->sizeConverter->convert( 134 $properties['PADDING-TOP'], 135 $this->mpdf->blk[$this->mpdf->blklvl]['inner_width'], 136 $this->mpdf->FontSize, 137 false 138 ); 139 } 140 if (isset($properties['PADDING-BOTTOM'])) { 141 $objattr['padding_bottom'] = $this->sizeConverter->convert( 142 $properties['PADDING-BOTTOM'], 143 $this->mpdf->blk[$this->mpdf->blklvl]['inner_width'], 144 $this->mpdf->FontSize, 145 false 146 ); 147 } 148 if (isset($properties['PADDING-LEFT'])) { 149 $objattr['padding_left'] = $this->sizeConverter->convert( 150 $properties['PADDING-LEFT'], 151 $this->mpdf->blk[$this->mpdf->blklvl]['inner_width'], 152 $this->mpdf->FontSize, 153 false 154 ); 155 } 156 if (isset($properties['PADDING-RIGHT'])) { 157 $objattr['padding_right'] = $this->sizeConverter->convert( 158 $properties['PADDING-RIGHT'], 159 $this->mpdf->blk[$this->mpdf->blklvl]['inner_width'], 160 $this->mpdf->FontSize, 161 false 162 ); 163 } 164 165 if (isset($properties['BORDER-TOP'])) { 166 $objattr['border_top'] = $this->mpdf->border_details($properties['BORDER-TOP']); 167 } 168 if (isset($properties['BORDER-BOTTOM'])) { 169 $objattr['border_bottom'] = $this->mpdf->border_details($properties['BORDER-BOTTOM']); 170 } 171 if (isset($properties['BORDER-LEFT'])) { 172 $objattr['border_left'] = $this->mpdf->border_details($properties['BORDER-LEFT']); 173 } 174 if (isset($properties['BORDER-RIGHT'])) { 175 $objattr['border_right'] = $this->mpdf->border_details($properties['BORDER-RIGHT']); 176 } 177 178 if (isset($properties['VERTICAL-ALIGN'])) { 179 $objattr['vertical-align'] = $this->getAlign($properties['VERTICAL-ALIGN']); 180 } 181 $w = 0; 182 $h = 0; 183 if (isset($properties['WIDTH'])) { 184 $w = $this->sizeConverter->convert( 185 $properties['WIDTH'], 186 $this->mpdf->blk[$this->mpdf->blklvl]['inner_width'], 187 $this->mpdf->FontSize, 188 false 189 ); 190 } elseif (isset($attr['WIDTH'])) { 191 $w = $this->sizeConverter->convert($attr['WIDTH'], $this->mpdf->blk[$this->mpdf->blklvl]['inner_width'], $this->mpdf->FontSize, false); 192 } 193 194 if (isset($properties['HEIGHT'])) { 195 $h = $this->sizeConverter->convert( 196 $properties['HEIGHT'], 197 $this->mpdf->blk[$this->mpdf->blklvl]['inner_width'], 198 $this->mpdf->FontSize, 199 false 200 ); 201 } elseif (isset($attr['HEIGHT'])) { 202 $h = $this->sizeConverter->convert($attr['HEIGHT'], $this->mpdf->blk[$this->mpdf->blklvl]['inner_width'], $this->mpdf->FontSize, false); 203 } 204 205 if (isset($properties['OPACITY']) && $properties['OPACITY'] > 0 && $properties['OPACITY'] <= 1) { 206 $objattr['opacity'] = $properties['OPACITY']; 207 } 208 if ($this->mpdf->HREF) { 209 if (strpos($this->mpdf->HREF, '.') === false && strpos($this->mpdf->HREF, '@') !== 0) { 210 $href = $this->mpdf->HREF; 211 while (array_key_exists($href, $this->mpdf->internallink)) { 212 $href = '#' . $href; 213 } 214 $this->mpdf->internallink[$href] = $this->mpdf->AddLink(); 215 $objattr['link'] = $this->mpdf->internallink[$href]; 216 } else { 217 $objattr['link'] = $this->mpdf->HREF; 218 } 219 } 220 $extraheight = $objattr['padding_top'] + $objattr['padding_bottom'] + $objattr['margin_top'] 221 + $objattr['margin_bottom'] + $objattr['border_top']['w'] + $objattr['border_bottom']['w']; 222 223 $extrawidth = $objattr['padding_left'] + $objattr['padding_right'] + $objattr['margin_left'] 224 + $objattr['margin_right'] + $objattr['border_left']['w'] + $objattr['border_right']['w']; 225 226 $svg = $this->makeSVG($type, $value, $max, $min, $optimum, $low, $high); 227 //Save to local file 228 $srcpath = $this->cache->write('/_tempSVG' . uniqid(random_int(1, 100000), true) . '_' . strtolower($tag) . '.svg', $svg); 229 $orig_srcpath = $srcpath; 230 $this->mpdf->GetFullPath($srcpath); 231 232 $info = $this->imageProcessor->getImage($srcpath, true, true, $orig_srcpath); 233 if (!$info) { 234 $info = $this->imageProcessor->getImage($this->mpdf->noImageFile); 235 if ($info) { 236 $srcpath = $this->mpdf->noImageFile; 237 $w = ($info['w'] * (25.4 / $this->mpdf->img_dpi)); 238 $h = ($info['h'] * (25.4 / $this->mpdf->img_dpi)); 239 } 240 } 241 if (!$info) { 242 return; 243 } 244 245 $objattr['file'] = $srcpath; 246 247 // Default width and height calculation if needed 248 if ($w == 0 && $h == 0) { 249 // SVG units are pixels 250 $w = $this->mpdf->FontSize / (10 / Mpdf::SCALE) * abs($info['w']) / Mpdf::SCALE; 251 $h = $this->mpdf->FontSize / (10 / Mpdf::SCALE) * abs($info['h']) / Mpdf::SCALE; 252 } 253 254 // IF WIDTH OR HEIGHT SPECIFIED 255 if ($w == 0) { 256 $w = $info['h'] ? abs($h * $info['w'] / $info['h']) : INF; 257 } 258 if ($h == 0) { 259 $h = $info['w'] ? abs($w * $info['h'] / $info['w']) : INF; 260 } 261 262 // Resize to maximum dimensions of page 263 $maxWidth = $this->mpdf->blk[$this->mpdf->blklvl]['inner_width']; 264 $maxHeight = $this->mpdf->h - ($this->mpdf->tMargin + $this->mpdf->bMargin + 1); 265 if ($this->mpdf->fullImageHeight) { 266 $maxHeight = $this->mpdf->fullImageHeight; 267 } 268 if (($w + $extrawidth) > ($maxWidth + 0.0001)) { // mPDF 5.7.4 0.0001 to allow for rounding errors when w==maxWidth 269 $w = $maxWidth - $extrawidth; 270 $h = abs($w * $info['h'] / $info['w']); 271 } 272 273 if ($h + $extraheight > $maxHeight) { 274 $h = $maxHeight - $extraheight; 275 $w = abs($h * $info['w'] / $info['h']); 276 } 277 $objattr['type'] = 'image'; 278 $objattr['itype'] = $info['type']; 279 280 $objattr['orig_h'] = $info['h']; 281 $objattr['orig_w'] = $info['w']; 282 $objattr['wmf_x'] = $info['x']; 283 $objattr['wmf_y'] = $info['y']; 284 $objattr['height'] = $h + $extraheight; 285 $objattr['width'] = $w + $extrawidth; 286 $objattr['image_height'] = $h; 287 $objattr['image_width'] = $w; 288 $e = "\xbb\xa4\xactype=image,objattr=" . serialize($objattr) . "\xbb\xa4\xac"; 289 if ($this->mpdf->tableLevel) { 290 $this->mpdf->_saveCellTextBuffer($e, $this->mpdf->HREF); 291 $this->mpdf->cell[$this->mpdf->row][$this->mpdf->col]['s'] += $objattr['width']; 292 } else { 293 $this->mpdf->_saveTextBuffer($e, $this->mpdf->HREF); 294 } 295 } 296 297 public function close(&$ahtml, &$ihtml) 298 { 299 parent::close($ahtml, $ihtml); 300 $this->mpdf->ignorefollowingspaces = false; 301 $this->mpdf->inMeter = false; 302 } 303 304 protected function makeSVG($type, $value, $max, $min, $optimum, $low, $high) 305 { 306 if ($type == '2') { 307 ///////////////////////////////////////////////////////////////////////////////////// 308 ///////// CUSTOM <meter type="2"> 309 ///////////////////////////////////////////////////////////////////////////////////// 310 $h = 10; 311 $w = 160; 312 $border_radius = 0.143; // Factor of Height 313 314 $svg = '<?xml version="1.0" encoding="UTF-8"?> 315<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> 316<svg width="' . $w . 'px" height="' . $h . 'px" viewBox="0 0 ' . $w . ' ' . $h . '" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ><g> 317 318 319<defs> 320<linearGradient id="GrGRAY" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox"> 321<stop offset="0%" stop-color="rgb(222, 222, 222)" /> 322<stop offset="20%" stop-color="rgb(232, 232, 232)" /> 323<stop offset="25%" stop-color="rgb(232, 232, 232)" /> 324<stop offset="100%" stop-color="rgb(182, 182, 182)" /> 325</linearGradient> 326 327</defs> 328'; 329 $svg .= '<rect x="0" y="0" width="' . $w . '" height="' . $h . '" fill="#f4f4f4" stroke="none" />'; 330 331 // LOW to HIGH region 332 //if ($low && $high && ($low != $min || $high != $max)) { 333 if ($low && $high) { 334 $barx = (($low - $min) / ($max - $min) ) * $w; 335 $barw = (($high - $low) / ($max - $min) ) * $w; 336 $svg .= '<rect x="' . $barx . '" y="0" width="' . $barw . '" height="' . $h . '" fill="url(#GrGRAY)" stroke="#888888" stroke-width="0.5px" />'; 337 } 338 339 // OPTIMUM Marker (? AVERAGE) 340 if ($optimum) { 341 $barx = (($optimum - $min) / ($max - $min) ) * $w; 342 $barw = $h / 2; 343 $barcol = '#888888'; 344 $svg .= '<rect x="' . $barx . '" y="0" rx="' . ($h * $border_radius) . 'px" ry="' . ($h * $border_radius) . 'px" width="' . $barw . '" height="' . $h . '" fill="' . $barcol . '" stroke="none" />'; 345 } 346 347 // VALUE Marker 348 if ($value) { 349 if ($min != $low && $value < $low) { 350 $col = 'orange'; 351 } elseif ($max != $high && $value > $high) { 352 $col = 'orange'; 353 } else { 354 $col = '#008800'; 355 } 356 $cx = (($value - $min) / ($max - $min) ) * $w; 357 $cy = $h / 2; 358 $rx = $h / 3.5; 359 $ry = $h / 2.2; 360 $svg .= '<ellipse fill="' . $col . '" stroke="#000000" stroke-width="0.5px" cx="' . $cx . '" cy="' . $cy . '" rx="' . $rx . '" ry="' . $ry . '"/>'; 361 } 362 363 // BoRDER 364 $svg .= '<rect x="0" y="0" width="' . $w . '" height="' . $h . '" fill="none" stroke="#888888" stroke-width="0.5px" />'; 365 366 $svg .= '</g></svg>'; 367 } elseif ($type == '3') { 368 ///////////////////////////////////////////////////////////////////////////////////// 369 ///////// CUSTOM <meter type="2"> 370 ///////////////////////////////////////////////////////////////////////////////////// 371 $h = 10; 372 $w = 100; 373 $border_radius = 0.143; // Factor of Height 374 375 $svg = '<?xml version="1.0" encoding="UTF-8"?> 376<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> 377<svg width="' . $w . 'px" height="' . $h . 'px" viewBox="0 0 ' . $w . ' ' . $h . '" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ><g> 378 379 380<defs> 381<linearGradient id="GrGRAY" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox"> 382<stop offset="0%" stop-color="rgb(222, 222, 222)" /> 383<stop offset="20%" stop-color="rgb(232, 232, 232)" /> 384<stop offset="25%" stop-color="rgb(232, 232, 232)" /> 385<stop offset="100%" stop-color="rgb(182, 182, 182)" /> 386</linearGradient> 387 388</defs> 389'; 390 $svg .= '<rect x="0" y="0" width="' . $w . '" height="' . $h . '" fill="#f4f4f4" stroke="none" />'; 391 392 // LOW to HIGH region 393 if ($low && $high && ($low != $min || $high != $max)) { 394 //if ($low && $high) { 395 $barx = (($low - $min) / ($max - $min) ) * $w; 396 $barw = (($high - $low) / ($max - $min) ) * $w; 397 $svg .= '<rect x="' . $barx . '" y="0" width="' . $barw . '" height="' . $h . '" fill="url(#GrGRAY)" stroke="#888888" stroke-width="0.5px" />'; 398 } 399 400 // OPTIMUM Marker (? AVERAGE) 401 if ($optimum) { 402 $barx = (($optimum - $min) / ($max - $min) ) * $w; 403 $barw = $h / 2; 404 $barcol = '#888888'; 405 $svg .= '<rect x="' . $barx . '" y="0" rx="' . ($h * $border_radius) . 'px" ry="' . ($h * $border_radius) . 'px" width="' . $barw . '" height="' . $h . '" fill="' . $barcol . '" stroke="none" />'; 406 } 407 408 // VALUE Marker 409 if ($value) { 410 if ($min != $low && $value < $low) { 411 $col = 'orange'; 412 } elseif ($max != $high && $value > $high) { 413 $col = 'orange'; 414 } else { 415 $col = 'orange'; 416 } 417 $cx = (($value - $min) / ($max - $min) ) * $w; 418 $cy = $h / 2; 419 $rx = $h / 2.2; 420 $ry = $h / 2.2; 421 $svg .= '<ellipse fill="' . $col . '" stroke="#000000" stroke-width="0.5px" cx="' . $cx . '" cy="' . $cy . '" rx="' . $rx . '" ry="' . $ry . '"/>'; 422 } 423 424 // BoRDER 425 $svg .= '<rect x="0" y="0" width="' . $w . '" height="' . $h . '" fill="none" stroke="#888888" stroke-width="0.5px" />'; 426 427 $svg .= '</g></svg>'; 428 } else { 429 ///////////////////////////////////////////////////////////////////////////////////// 430 ///////// DEFAULT <meter> 431 ///////////////////////////////////////////////////////////////////////////////////// 432 $h = 10; 433 $w = 50; 434 $border_radius = 0.143; // Factor of Height 435 436 $svg = '<?xml version="1.0" encoding="UTF-8"?> 437<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> 438<svg width="' . $w . 'px" height="' . $h . 'px" viewBox="0 0 ' . $w . ' ' . $h . '" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ><g> 439 440<defs> 441<linearGradient id="GrGRAY" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox"> 442<stop offset="0%" stop-color="rgb(222, 222, 222)" /> 443<stop offset="20%" stop-color="rgb(232, 232, 232)" /> 444<stop offset="25%" stop-color="rgb(232, 232, 232)" /> 445<stop offset="100%" stop-color="rgb(182, 182, 182)" /> 446</linearGradient> 447 448<linearGradient id="GrRED" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox"> 449<stop offset="0%" stop-color="rgb(255, 162, 162)" /> 450<stop offset="20%" stop-color="rgb(255, 218, 218)" /> 451<stop offset="25%" stop-color="rgb(255, 218, 218)" /> 452<stop offset="100%" stop-color="rgb(255, 0, 0)" /> 453</linearGradient> 454 455<linearGradient id="GrGREEN" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox"> 456<stop offset="0%" stop-color="rgb(102, 230, 102)" /> 457<stop offset="20%" stop-color="rgb(218, 255, 218)" /> 458<stop offset="25%" stop-color="rgb(218, 255, 218)" /> 459<stop offset="100%" stop-color="rgb(0, 148, 0)" /> 460</linearGradient> 461 462<linearGradient id="GrBLUE" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox"> 463<stop offset="0%" stop-color="rgb(102, 102, 230)" /> 464<stop offset="20%" stop-color="rgb(238, 238, 238)" /> 465<stop offset="25%" stop-color="rgb(238, 238, 238)" /> 466<stop offset="100%" stop-color="rgb(0, 0, 128)" /> 467</linearGradient> 468 469<linearGradient id="GrORANGE" x1="0" y1="0" x2="0" y2="1" gradientUnits="boundingBox"> 470<stop offset="0%" stop-color="rgb(255, 186, 0)" /> 471<stop offset="20%" stop-color="rgb(255, 238, 168)" /> 472<stop offset="25%" stop-color="rgb(255, 238, 168)" /> 473<stop offset="100%" stop-color="rgb(255, 155, 0)" /> 474</linearGradient> 475</defs> 476 477<rect x="0" y="0" rx="' . ($h * $border_radius) . 'px" ry="' . ($h * $border_radius) . 'px" width="' . $w . '" height="' . $h . '" fill="url(#GrGRAY)" stroke="none" /> 478'; 479 480 if ($value) { 481 $barw = (($value - $min) / ($max - $min) ) * $w; 482 if ($optimum < $low) { 483 if ($value < $low) { 484 $barcol = 'url(#GrGREEN)'; 485 } elseif ($value > $high) { 486 $barcol = 'url(#GrRED)'; 487 } else { 488 $barcol = 'url(#GrORANGE)'; 489 } 490 } elseif ($optimum > $high) { 491 if ($value < $low) { 492 $barcol = 'url(#GrRED)'; 493 } elseif ($value > $high) { 494 $barcol = 'url(#GrGREEN)'; 495 } else { 496 $barcol = 'url(#GrORANGE)'; 497 } 498 } else { 499 if ($value < $low) { 500 $barcol = 'url(#GrORANGE)'; 501 } elseif ($value > $high) { 502 $barcol = 'url(#GrORANGE)'; 503 } else { 504 $barcol = 'url(#GrGREEN)'; 505 } 506 } 507 $svg .= '<rect x="0" y="0" rx="' . ($h * $border_radius) . 'px" ry="' . ($h * $border_radius) . 'px" width="' . $barw . '" height="' . $h . '" fill="' . $barcol . '" stroke="none" />'; 508 } 509 510 $svg .= '</g></svg>'; 511 } 512 513 514 return $svg; 515 } 516 517} 518