1<?php 2 3namespace Mpdf; 4 5use Mpdf\Color\ColorConverter; 6use Mpdf\Css\TextVars; 7 8class DirectWrite 9{ 10 11 /** 12 * @var \Mpdf\Mpdf 13 */ 14 private $mpdf; 15 16 /** 17 * @var \Mpdf\Otl 18 */ 19 private $otl; 20 21 /** 22 * @var \Mpdf\SizeConverter 23 */ 24 private $sizeConverter; 25 26 /** 27 * @var \Mpdf\Color\ColorConverter 28 */ 29 private $colorConverter; 30 31 public function __construct(Mpdf $mpdf, Otl $otl, SizeConverter $sizeConverter, ColorConverter $colorConverter) 32 { 33 $this->mpdf = $mpdf; 34 $this->otl = $otl; 35 $this->sizeConverter = $sizeConverter; 36 $this->colorConverter = $colorConverter; 37 } 38 39 function Write($h, $txt, $currentx = 0, $link = '', $directionality = 'ltr', $align = '', $fill = 0) 40 { 41 if (!$align) { 42 if ($directionality === 'rtl') { 43 $align = 'R'; 44 } else { 45 $align = 'L'; 46 } 47 } 48 if ($h == 0) { 49 $this->mpdf->SetLineHeight(); 50 $h = $this->mpdf->lineheight; 51 } 52 //Output text in flowing mode 53 $w = $this->mpdf->w - $this->mpdf->rMargin - $this->mpdf->x; 54 55 $wmax = ($w - ($this->mpdf->cMarginL + $this->mpdf->cMarginR)); 56 $s = str_replace("\r", '', $txt); 57 if ($this->mpdf->usingCoreFont) { 58 $nb = strlen($s); 59 } else { 60 $nb = mb_strlen($s, $this->mpdf->mb_enc); 61 // handle single space character 62 if (($nb === 1) && $s === ' ') { 63 $this->mpdf->x += $this->mpdf->GetStringWidth($s); 64 return; 65 } 66 } 67 $sep = -1; 68 $i = 0; 69 $j = 0; 70 $l = 0; 71 $nl = 1; 72 if (!$this->mpdf->usingCoreFont) { 73 if (preg_match('/([' . $this->mpdf->pregRTLchars . '])/u', $txt)) { 74 $this->mpdf->biDirectional = true; 75 } // *RTL* 76 while ($i < $nb) { 77 //Get next character 78 $c = mb_substr($s, $i, 1, $this->mpdf->mb_enc); 79 if ($c === "\n") { 80 // WORD SPACING 81 $this->mpdf->ResetSpacing(); 82 //Explicit line break 83 $tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mpdf->mb_enc)); 84 $this->mpdf->Cell($w, $h, $tmp, 0, 2, $align, $fill, $link); 85 $i++; 86 $sep = -1; 87 $j = $i; 88 $l = 0; 89 if ($nl === 1) { 90 if ($currentx != 0) { 91 $this->mpdf->x = $currentx; 92 } else { 93 $this->mpdf->x = $this->mpdf->lMargin; 94 } 95 $w = $this->mpdf->w - $this->mpdf->rMargin - $this->mpdf->x; 96 $wmax = ($w - ($this->mpdf->cMarginL + $this->mpdf->cMarginR)); 97 } 98 $nl++; 99 continue; 100 } 101 if ($c === ' ') { 102 $sep = $i; 103 } 104 $l += $this->mpdf->GetCharWidthNonCore($c); // mPDF 5.3.04 105 if ($l > $wmax) { 106 //Automatic line break (word wrapping) 107 if ($sep == -1) { 108 // WORD SPACING 109 $this->mpdf->ResetSpacing(); 110 if ($this->mpdf->x > $this->mpdf->lMargin) { 111 //Move to next line 112 if ($currentx != 0) { 113 $this->mpdf->x = $currentx; 114 } else { 115 $this->mpdf->x = $this->mpdf->lMargin; 116 } 117 $this->mpdf->y+=$h; 118 $w = $this->mpdf->w - $this->mpdf->rMargin - $this->mpdf->x; 119 $wmax = ($w - ($this->mpdf->cMarginL + $this->mpdf->cMarginR)); 120 $i++; 121 $nl++; 122 continue; 123 } 124 if ($i == $j) { 125 $i++; 126 } 127 $tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mpdf->mb_enc)); 128 $this->mpdf->Cell($w, $h, $tmp, 0, 2, $align, $fill, $link); 129 } else { 130 $tmp = rtrim(mb_substr($s, $j, $sep - $j, $this->mpdf->mb_enc)); 131 132 if ($align === 'J') { 133 ////////////////////////////////////////// 134 // JUSTIFY J using Unicode fonts (Word spacing doesn't work) 135 // WORD SPACING 136 // Change NON_BREAKING SPACE to spaces so they are 'spaced' properly 137 $tmp = str_replace(chr(194) . chr(160), chr(32), $tmp); 138 $len_ligne = $this->mpdf->GetStringWidth($tmp); 139 $nb_carac = mb_strlen($tmp, $this->mpdf->mb_enc); 140 $nb_spaces = mb_substr_count($tmp, ' ', $this->mpdf->mb_enc); 141 $inclCursive = false; 142 if (!empty($this->mpdf->CurrentFont['useOTL']) && preg_match('/([' . $this->mpdf->pregCURSchars . '])/u', $tmp)) { 143 $inclCursive = true; 144 } 145 list($charspacing, $ws) = $this->mpdf->GetJspacing($nb_carac, $nb_spaces, (($w - 2) - $len_ligne) * Mpdf::SCALE, $inclCursive); 146 $this->mpdf->SetSpacing($charspacing, $ws); 147 ////////////////////////////////////////// 148 } 149 $this->mpdf->Cell($w, $h, $tmp, 0, 2, $align, $fill, $link); 150 $i = $sep + 1; 151 } 152 $sep = -1; 153 $j = $i; 154 $l = 0; 155 if ($nl === 1) { 156 if ($currentx != 0) { 157 $this->mpdf->x = $currentx; 158 } else { 159 $this->mpdf->x = $this->mpdf->lMargin; 160 } 161 $w = $this->mpdf->w - $this->mpdf->rMargin - $this->mpdf->x; 162 $wmax = ($w - ($this->mpdf->cMarginL + $this->mpdf->cMarginR)); 163 } 164 $nl++; 165 } else { 166 $i++; 167 } 168 } 169 //Last chunk 170 // WORD SPACING 171 $this->mpdf->ResetSpacing(); 172 } else { 173 while ($i < $nb) { 174 //Get next character 175 $c = $s[$i]; 176 if ($c === "\n") { 177 //Explicit line break 178 // WORD SPACING 179 $this->mpdf->ResetSpacing(); 180 $this->mpdf->Cell($w, $h, substr($s, $j, $i - $j), 0, 2, $align, $fill, $link); 181 $i++; 182 $sep = -1; 183 $j = $i; 184 $l = 0; 185 if ($nl === 1) { 186 if ($currentx != 0) { 187 $this->mpdf->x = $currentx; 188 } else { 189 $this->mpdf->x = $this->mpdf->lMargin; 190 } 191 $w = $this->mpdf->w - $this->mpdf->rMargin - $this->mpdf->x; 192 $wmax = $w - ($this->mpdf->cMarginL + $this->mpdf->cMarginR); 193 } 194 $nl++; 195 continue; 196 } 197 if ($c === ' ') { 198 $sep = $i; 199 } 200 $l += $this->mpdf->GetCharWidthCore($c); // mPDF 5.3.04 201 if ($l > $wmax) { 202 //Automatic line break (word wrapping) 203 if ($sep == -1) { 204 // WORD SPACING 205 $this->mpdf->ResetSpacing(); 206 if ($this->mpdf->x > $this->mpdf->lMargin) { 207 //Move to next line 208 if ($currentx != 0) { 209 $this->mpdf->x = $currentx; 210 } else { 211 $this->mpdf->x = $this->mpdf->lMargin; 212 } 213 $this->mpdf->y+=$h; 214 $w = $this->mpdf->w - $this->mpdf->rMargin - $this->mpdf->x; 215 $wmax = $w - ($this->mpdf->cMarginL + $this->mpdf->cMarginR); 216 $i++; 217 $nl++; 218 continue; 219 } 220 if ($i == $j) { 221 $i++; 222 } 223 $this->mpdf->Cell($w, $h, substr($s, $j, $i - $j), 0, 2, $align, $fill, $link); 224 } else { 225 $tmp = substr($s, $j, $sep - $j); 226 if ($align === 'J') { 227 ////////////////////////////////////////// 228 // JUSTIFY J using Unicode fonts 229 // WORD SPACING is not fully supported for complex scripts 230 // Change NON_BREAKING SPACE to spaces so they are 'spaced' properly 231 $tmp = str_replace(chr(160), chr(32), $tmp); 232 $len_ligne = $this->mpdf->GetStringWidth($tmp); 233 $nb_carac = strlen($tmp); 234 $nb_spaces = substr_count($tmp, ' '); 235 list($charspacing, $ws) = $this->mpdf->GetJspacing($nb_carac, $nb_spaces, (($w - 2) - $len_ligne) * Mpdf::SCALE, $false); 236 $this->mpdf->SetSpacing($charspacing, $ws); 237 ////////////////////////////////////////// 238 } 239 $this->mpdf->Cell($w, $h, $tmp, 0, 2, $align, $fill, $link); 240 $i = $sep + 1; 241 } 242 $sep = -1; 243 $j = $i; 244 $l = 0; 245 if ($nl === 1) { 246 if ($currentx != 0) { 247 $this->mpdf->x = $currentx; 248 } else { 249 $this->mpdf->x = $this->mpdf->lMargin; 250 } 251 $w = $this->mpdf->w - $this->mpdf->rMargin - $this->mpdf->x; 252 $wmax = $w - ($this->mpdf->cMarginL + $this->mpdf->cMarginR); 253 } 254 $nl++; 255 } else { 256 $i++; 257 } 258 } 259 // WORD SPACING 260 $this->mpdf->ResetSpacing(); 261 } 262 //Last chunk 263 if ($i != $j) { 264 if ($currentx != 0) { 265 $this->mpdf->x = $currentx; 266 } else { 267 $this->mpdf->x = $this->mpdf->lMargin; 268 } 269 if ($this->mpdf->usingCoreFont) { 270 $tmp = substr($s, $j, $i - $j); 271 } else { 272 $tmp = mb_substr($s, $j, $i - $j, $this->mpdf->mb_enc); 273 } 274 $this->mpdf->Cell($w, $h, $tmp, 0, 0, $align, $fill, $link); 275 } 276 } 277 278 function CircularText($x, $y, $r, $text, $align = 'top', $fontfamily = '', $fontsizePt = 0, $fontstyle = '', $kerning = 120, $fontwidth = 100, $divider = '') 279 { 280 if ($fontfamily || $fontstyle || $fontsizePt) { 281 $this->mpdf->SetFont($fontfamily, $fontstyle, $fontsizePt); 282 } 283 284 $kerning /= 100; 285 $fontwidth /= 100; 286 287 if ($kerning == 0) { 288 throw new \Mpdf\MpdfException('Please use values unequal to zero for kerning (CircularText)'); 289 } 290 291 if ($fontwidth == 0) { 292 throw new \Mpdf\MpdfException('Please use values unequal to zero for font width (CircularText)'); 293 } 294 295 $text = str_replace("\r", '', $text); 296 297 // circumference 298 $u = ($r * 2) * M_PI; 299 $checking = true; 300 $autoset = false; 301 302 while ($checking) { 303 $t = 0; 304 $w = []; 305 if ($this->mpdf->usingCoreFont) { 306 $nb = strlen($text); 307 for ($i = 0; $i < $nb; $i++) { 308 $w[$i] = $this->mpdf->GetStringWidth($text[$i]); 309 $w[$i]*=$kerning * $fontwidth; 310 $t+=$w[$i]; 311 } 312 } else { 313 $nb = mb_strlen($text, $this->mpdf->mb_enc); 314 $lastchar = ''; 315 $unicode = $this->mpdf->UTF8StringToArray($text); 316 for ($i = 0; $i < $nb; $i++) { 317 $c = mb_substr($text, $i, 1, $this->mpdf->mb_enc); 318 $w[$i] = $this->mpdf->GetStringWidth($c); 319 $w[$i]*=$kerning * $fontwidth; 320 $char = $unicode[$i]; 321 if ($this->mpdf->useKerning && $lastchar && isset($this->mpdf->CurrentFont['kerninfo'][$lastchar][$char])) { 322 $tk = $this->mpdf->CurrentFont['kerninfo'][$lastchar][$char] * ($this->mpdf->FontSize / 1000) * $kerning * $fontwidth; 323 $w[$i] += $tk / 2; 324 $w[$i - 1] += $tk / 2; 325 $t+=$tk; 326 } 327 $lastchar = $char; 328 $t+=$w[$i]; 329 } 330 } 331 if ($fontsizePt >= 0 || $autoset) { 332 $checking = false; 333 } else { 334 $t+=$this->mpdf->GetStringWidth(' '); 335 if ($divider) { 336 $t+=$this->mpdf->GetStringWidth(' '); 337 } 338 if ($fontsizePt == -2) { 339 $fontsizePt = $this->mpdf->FontSizePt * 0.5 * $u / $t; 340 } else { 341 $fontsizePt = $this->mpdf->FontSizePt * $u / $t; 342 } 343 $this->mpdf->SetFontSize($fontsizePt); 344 $autoset = true; 345 } 346 } 347 348 // total width of string in degrees 349 $d = ($t / $u) * 360; 350 351 $this->mpdf->StartTransform(); 352 353 // rotate matrix for the first letter to center the text 354 // (half of total degrees) 355 if ($align === 'top') { 356 $this->mpdf->transformRotate(-$d / 2, $x, $y); 357 } else { 358 $this->mpdf->transformRotate($d / 2, $x, $y); 359 } 360 361 // run through the string 362 for ($i = 0; $i < $nb; $i++) { 363 364 if ($align === 'top') { 365 366 // rotate matrix half of the width of current letter + half of the width of preceding letter 367 if ($i === 0) { 368 $this->mpdf->transformRotate((($w[$i] / 2) / $u) * 360, $x, $y); 369 } else { 370 $this->mpdf->transformRotate((($w[$i] / 2 + $w[$i - 1] / 2) / $u) * 360, $x, $y); 371 } 372 373 if ($fontwidth !== 1) { 374 $this->mpdf->StartTransform(); 375 $this->mpdf->transformScale($fontwidth * 100, 100, $x, $y); 376 } 377 378 $this->mpdf->SetXY($x - $w[$i] / 2, $y - $r); 379 380 } else { 381 382 // rotate matrix half of the width of current letter + half of the width of preceding letter 383 if ($i === 0) { 384 $this->mpdf->transformRotate(-(($w[$i] / 2) / $u) * 360, $x, $y); 385 } else { 386 $this->mpdf->transformRotate(-(($w[$i] / 2 + $w[$i - 1] / 2) / $u) * 360, $x, $y); 387 } 388 389 if ($fontwidth !== 1) { 390 $this->mpdf->StartTransform(); 391 $this->mpdf->transformScale($fontwidth * 100, 100, $x, $y); 392 } 393 $this->mpdf->SetXY($x - $w[$i] / 2, $y + $r - $this->mpdf->FontSize); 394 } 395 396 if ($this->mpdf->usingCoreFont) { 397 $c = $text[$i]; 398 } else { 399 $c = mb_substr($text, $i, 1, $this->mpdf->mb_enc); 400 } 401 402 $this->mpdf->Cell($w[$i], $this->mpdf->FontSize, $c, 0, 0, 'C'); // mPDF 5.3.53 403 404 if ($fontwidth !== 1) { 405 $this->mpdf->StopTransform(); 406 } 407 } 408 409 $this->mpdf->StopTransform(); 410 411 // mPDF 5.5.23 412 if ($align === 'top' && $divider != '') { 413 $wc = $this->mpdf->GetStringWidth($divider); 414 $wc *= $kerning * $fontwidth; 415 416 $this->mpdf->StartTransform(); 417 $this->mpdf->transformRotate(90, $x, $y); 418 $this->mpdf->SetXY($x - $wc / 2, $y - $r); 419 $this->mpdf->Cell($wc, $this->mpdf->FontSize, $divider, 0, 0, 'C'); 420 $this->mpdf->StopTransform(); 421 422 $this->mpdf->StartTransform(); 423 $this->mpdf->transformRotate(-90, $x, $y); 424 $this->mpdf->SetXY($x - $wc / 2, $y - $r); 425 $this->mpdf->Cell($wc, $this->mpdf->FontSize, $divider, 0, 0, 'C'); 426 $this->mpdf->StopTransform(); 427 } 428 } 429 430 function Shaded_box($text, $font = '', $fontstyle = 'B', $szfont = '', $width = '70%', $style = 'DF', $radius = 2.5, $fill = '#FFFFFF', $color = '#000000', $pad = 2) 431 { 432 // F (shading - no line),S (line, no shading),DF (both) 433 if (!$font) { 434 $font = $this->mpdf->default_font; 435 } 436 if (!$szfont) { 437 $szfont = $this->mpdf->default_font_size * 1.8; 438 } 439 440 $text = ' ' . $text . ' '; 441 $this->mpdf->SetFont($font, $fontstyle, $szfont, false); 442 443 $text = $this->mpdf->purify_utf8_text($text); 444 445 if ($this->mpdf->text_input_as_HTML) { 446 $text = $this->mpdf->all_entities_to_utf8($text); 447 } 448 449 if ($this->mpdf->usingCoreFont) { 450 $text = mb_convert_encoding($text, $this->mpdf->mb_enc, 'UTF-8'); 451 } 452 453 454 // DIRECTIONALITY 455 if (preg_match('/([' . $this->mpdf->pregRTLchars . '])/u', $text)) { 456 $this->mpdf->biDirectional = true; 457 } // *RTL* 458 459 $textvar = 0; 460 $save_OTLtags = $this->mpdf->OTLtags; 461 $this->mpdf->OTLtags = []; 462 463 if ($this->mpdf->useKerning) { 464 if ($this->mpdf->CurrentFont['haskernGPOS']) { 465 $this->mpdf->OTLtags['Plus'] .= ' kern'; 466 } else { 467 $textvar |= TextVars::FC_KERNING; 468 } 469 } 470 // Use OTL OpenType Table Layout - GSUB & GPOS 471 if (!empty($this->mpdf->CurrentFont['useOTL'])) { 472 $text = $this->otl->applyOTL($text, $this->mpdf->CurrentFont['useOTL']); 473 $OTLdata = $this->otl->OTLdata; 474 } 475 $this->mpdf->OTLtags = $save_OTLtags; 476 477 $this->mpdf->magic_reverse_dir($text, $this->mpdf->directionality, $OTLdata); 478 479 if (!$width) { 480 $width = $this->mpdf->pgwidth; 481 } else { 482 $width = $this->sizeConverter->convert($width, $this->mpdf->pgwidth); 483 } 484 $midpt = $this->mpdf->lMargin + ($this->mpdf->pgwidth / 2); 485 $r1 = $midpt - ($width / 2); //($this->mpdf->w / 2) - 40; 486 $r2 = $r1 + $width; //$r1 + 80; 487 $y1 = $this->mpdf->y; 488 489 $loop = 0; 490 491 while ($loop === 0) { 492 $this->mpdf->SetFont($font, $fontstyle, $szfont, false); 493 $sz = $this->mpdf->GetStringWidth($text, true, $OTLdata, $textvar); 494 if (($r1 + $sz) > $r2) { 495 $szfont --; 496 } else { 497 $loop ++; 498 } 499 } 500 $this->mpdf->SetFont($font, $fontstyle, $szfont, true, true); 501 502 $y2 = $this->mpdf->FontSize + ($pad * 2); 503 504 $this->mpdf->SetLineWidth(0.1); 505 $fc = $this->colorConverter->convert($fill, $this->mpdf->PDFAXwarnings); 506 $tc = $this->colorConverter->convert($color, $this->mpdf->PDFAXwarnings); 507 $this->mpdf->SetFColor($fc); 508 $this->mpdf->SetTColor($tc); 509 $this->mpdf->RoundedRect($r1, $y1, $r2 - $r1, $y2, $radius, $style); 510 $this->mpdf->SetX($r1); 511 $this->mpdf->Cell($r2 - $r1, $y2, $text, 0, 1, 'C', 0, '', 0, 0, 0, 'M', 0, false, $OTLdata, $textvar); 512 $this->mpdf->SetY($y1 + $y2 + 2); // +2 = mm margin below shaded box 513 $this->mpdf->Reset(); 514 } 515} 516