Poster.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | Niucloud-admin 企业快速开发的saas管理平台
  4. // +----------------------------------------------------------------------
  5. // | 官方网址:https://www.niucloud.com
  6. // +----------------------------------------------------------------------
  7. // | niucloud团队 版权所有 开源版本可自由商用
  8. // +----------------------------------------------------------------------
  9. // | Author: Niucloud Team
  10. // +----------------------------------------------------------------------
  11. namespace core\poster;
  12. use Kkokk\Poster\Facades\Poster as PosterInstance;
  13. class Poster extends BasePoster
  14. {
  15. /**
  16. * @param array $config
  17. * @return void
  18. */
  19. protected function initialize(array $config = [])
  20. {
  21. parent::initialize($config);
  22. }
  23. /**
  24. * 创建海报
  25. * @param array $poster_data
  26. * @param string $dir
  27. * @param string $file_path
  28. * @return mixed|string
  29. * @throws \Exception
  30. */
  31. public function createPoster(array $poster_data, string $dir, string $file_path)
  32. {
  33. $bg_type = $poster_data[ 'global' ][ 'bgType' ];
  34. $instance = PosterInstance::extension('gd')->config([ 'path' => realpath($dir) . DIRECTORY_SEPARATOR . $file_path ]);
  35. $bg_width = $poster_data[ 'global' ][ 'width' ];
  36. $bg_height = $poster_data[ 'global' ][ 'height' ];
  37. if ($bg_type == 'url' && !empty($poster_data[ 'global' ][ 'bgUrl' ]) && is_file($poster_data[ 'global' ][ 'bgUrl' ])) {
  38. $im = $instance->buildIm($bg_width, $bg_height)->buildImage([
  39. 'src' => $poster_data[ 'global' ][ 'bgUrl' ],
  40. // 'angle' => 80
  41. ], 0, 0, 0, 0, $bg_width, $bg_height);
  42. } else {
  43. $im = $instance->buildIm($bg_width, $bg_height, $this->getRgbColor($poster_data[ 'global' ][ 'bgColor' ]));
  44. }
  45. $align_array = [
  46. 'center', 'left', 'right', 'top', 'bottom'
  47. ];
  48. foreach ($poster_data[ 'value' ] as $k => $v) {
  49. $type = $v[ 'type' ];
  50. switch ($type) {
  51. case 'text':
  52. $font_size = ceil($v[ 'fontSize' ]);
  53. $default_font = 'static' . DIRECTORY_SEPARATOR . 'font' . DIRECTORY_SEPARATOR . 'SourceHanSansCN-Regular.ttf';
  54. $font = $v[ 'fontFamily' ] ? : $default_font;
  55. $content_list = $this->getText($v[ 'value' ], $font_size, $font, $v[ 'space' ] ?? 0, $v[ 'width' ], $v[ 'height' ], $v[ 'lineHeight' ] + $font_size);
  56. $base_y = $this->getX($v[ 'y' ]);
  57. if (is_array($base_y)) {
  58. $diff_height = count($content_list) * ( $v[ 'lineHeight' ] + $font_size );
  59. $again_y = $base_y[ 0 ];
  60. if ($again_y == 'center') {
  61. $base_y_num = ( $bg_height - $diff_height ) > 0 ? ( $bg_height - $diff_height ) / 2 : 0;
  62. } else if ($again_y == 'top') {
  63. $base_y_num = 0;
  64. } else {
  65. $base_y_num = $bg_height - $v[ 'height' ];
  66. }
  67. } else {
  68. $base_y_num = $base_y;
  69. }
  70. // if(in_array($base_y, $align_array)){
  71. // $diff_height = count($content_list)*($v[ 'lineHeight' ]+$font_size);
  72. // $base_y_num = ($bg_height-$diff_height) > 0 ? ($bg_height-$diff_height)/2 : 0;
  73. // }else{
  74. // $base_y_num = $base_y[0];
  75. // }
  76. foreach ($content_list as $ck => $content) {
  77. if ($ck == 0) {
  78. if ($v[ 'lineHeight' ] > 0) {
  79. $item_line = $v[ 'lineHeight' ] / 2;
  80. } else {
  81. $item_line = 0;
  82. }
  83. } else {
  84. $item_line = $v[ 'lineHeight' ] + $font_size;
  85. }
  86. $base_y_num += $item_line;
  87. //计算文本框宽度
  88. $im = $im->buildText($content, $this->getX($v[ 'x' ]), $base_y_num, $font_size, $this->getRgbColor($v[ 'fontColor' ]), $v[ 'width' ], $font, $v[ 'weight' ] ? 10 : null); # 合成文字
  89. }
  90. break;
  91. case 'image':
  92. if (is_file($v[ 'value' ])) {
  93. $im = $im->buildImage($v[ 'value' ], $this->getX($v[ 'x' ]), $this->getY($v[ 'y' ]), 0, 0, $v[ 'width' ], $v[ 'height' ], false, $v[ 'shape' ] ?? 'normal'); # 合成图片
  94. }
  95. break;
  96. case 'draw':
  97. if (!empty($v[ 'draw_type' ]) && $v[ 'draw_type' ] == 'Polygon') {
  98. $points = $v[ 'points' ];
  99. $im = $im->buildLine($points[ 0 ][ 0 ], $points[ 0 ][ 1 ], $points[ 2 ][ 0 ], $points[ 2 ][ 1 ], $this->getRgbColor($v[ 'bgColor' ]), 'filled_rectangle');
  100. }
  101. break;
  102. }
  103. }
  104. $path = $im->getPoster();
  105. return str_replace(DIRECTORY_SEPARATOR, '/', str_replace(realpath(''), '', $path[ 'url' ]));
  106. }
  107. public function getX($dst_x)
  108. {
  109. if (is_int($dst_x)) {
  110. return $dst_x;
  111. } else {
  112. return [ $dst_x, 0 ];
  113. }
  114. }
  115. public function getY($dst_y)
  116. {
  117. if (is_int($dst_y)) {
  118. return $dst_y;
  119. } else {
  120. return [ $dst_y, 0 ];
  121. }
  122. }
  123. public function getRgbColor($color)
  124. {
  125. $color = $color ? : '#FFFFFF';
  126. if (!str_contains($color, '#')) {
  127. $color = str_replace('rgba(', '', $color);
  128. $color = str_replace(')', '', $color);
  129. list($r, $g, $b) = explode(',', $color);
  130. list($r, $g, $b) = array_map(function($v) { return (int) $v; }, [ $r, $g, $b ]);
  131. } else {
  132. list($r, $g, $b) = sscanf($color, "#%02x%02x%02x");
  133. }
  134. return [ $r, $g, $b, 1 ];
  135. }
  136. /**
  137. * 获取高度限制过后的文本
  138. * @param $content
  139. * @param $fontSize
  140. * @param $font
  141. * @param $space
  142. * @param $max_ws
  143. * @param $max_hs
  144. * @param $line_height
  145. * @return mixed
  146. */
  147. public function getText($content, $fontSize, $font, $space, $max_ws, $max_hs, $line_height)
  148. {
  149. $calcSpace = $space > $fontSize ? ( $space - $fontSize ) : 0; // 获取间距计算值
  150. $fontSize = ( $fontSize * 3 ) / 4; // px 转化为 pt
  151. mb_internal_encoding('UTF-8'); // 设置编码
  152. // 这几个变量分别是 字体大小, 角度, 字体名称, 字符串, 预设宽度
  153. $contents = '';
  154. $contentsArr = [];
  155. $letter = [];
  156. $line = 1;
  157. $calcSpaceRes = 0;
  158. // 将字符串拆分成一个个单字 保存到数组 letter 中
  159. for ($i = 0; $i < mb_strlen($content); $i++) {
  160. $letter[] = mb_substr($content, $i, 1);
  161. }
  162. $textWidthArr = [];
  163. $contentStr = '';
  164. $line_num = 1;
  165. $total_width = 0;
  166. $content_list = [];
  167. foreach ($letter as $l) {
  168. $textStr = $contentStr . $l;
  169. $fontBox = imagettfbbox($fontSize, 0, $font, $textStr);
  170. $textWidth = abs($fontBox[ 2 ]) + $calcSpaceRes;
  171. $textWidthArr[ $line ] = $textWidth;
  172. // 判断拼接后的字符串是否超过预设的宽度
  173. if (( $textWidth > $max_ws ) && ( $contents !== '' )) {
  174. $line_num++;
  175. if (( $line_num * $line_height ) > $max_hs) {
  176. break;
  177. }
  178. $contents .= "\n";
  179. $contentStr = "";
  180. $line++;
  181. }
  182. if (empty($content_list[ $line_num - 1 ])) $content_list[ $line_num - 1 ] = '';
  183. $content_list[ $line_num - 1 ] .= $l;
  184. $contents .= $l;
  185. $contentStr .= $l;
  186. $line === 1 && $calcSpaceRes += $calcSpace;
  187. $text_width = max(array_values($textWidthArr));
  188. }
  189. return $content_list;
  190. }
  191. }