qrcode.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. <?php
  2. require_once(__DIR__.'/qrlib.php');
  3. /*
  4. //调用示例
  5. // include_once(APP_PATH.'/extend/phpqrcode/qrcode.php');
  6. // $qrcode=CreateQRCode('http://www.baidu.com/','400','D:\www\WhatPHPv2019\uploads\images\20190211\1549868835zDfBVGhpEC9893.jpg');
  7. */
  8. //创建一个二维码 $data:生成二维码包含的数据 $width:生成二维码的宽度 $title是否在二维码下方加文字 $logofile:生成二维码是否生成加入LOGO $savefile:保存到文件
  9. function CreateQRCode($data,$width,$title="",$logofile="",$issave=true,$savefile="")
  10. {
  11. //二维码图片保存路径(若不生成文件则设置为false)
  12. $filename = "qrcode_" . time() . ".png";
  13. //二维码容错率,默认L
  14. $level = "L";
  15. //二维码图片每个黑点的像素,默认4
  16. $size = get_qrcode_size($width);
  17. //二维码边框的间距,默认2
  18. $padding = 2;
  19. //保存二维码图片并显示出来,$filename必须传递文件路径
  20. $saveandprint = false;
  21. if($issave==true){
  22. if($savefile==""){
  23. $DIR1="/uploads/qrcode/".MyDate('Ymd',time())."/";
  24. $FILENAME="QR_".time().GetRandStr(20).".jpg";
  25. $path=ROOT_PATH.$DIR1;
  26. if(!file_exists($path)){
  27. mkdir($path,0777,true);
  28. }
  29. $savefile=$path.$FILENAME;
  30. $return_path=$DIR1.$FILENAME;
  31. }else{
  32. $return_path="";
  33. }
  34. //生成二维码图片
  35. \QRcode::png($data,$savefile,$level,$size,$padding,$saveandprint);
  36. if($logofile!=""){
  37. QRCodeLogo($savefile,$logofile);
  38. }
  39. if($title!=""){
  40. QRCodeTitle($savefile,$title);
  41. }
  42. return $return_path;
  43. }else{
  44. //生成二维码图片
  45. \QRcode::png($data,false,$level,$size,$padding,$saveandprint);
  46. }
  47. }
  48. //根据宽度计算二维码大小值
  49. function get_qrcode_size($w)
  50. {
  51. $size=floor($w/29*100)/100 + 0.01;
  52. return $size;
  53. }
  54. //给二维码中加入LOGO,并覆盖到二维码文件中
  55. function QRCodeLogo($Qrfile,$logofile)
  56. {
  57. $QR = imagecreatefromstring(file_get_contents($Qrfile)); //目标图象连接资源。
  58. $logo = imagecreatefromstring(file_get_contents($logofile)); //源图象连接资源。
  59. $QR_width = imagesx($QR); //二维码图片宽度
  60. $QR_height = imagesy($QR); //二维码图片高度
  61. $logo_width = imagesx($logo); //logo图片宽度
  62. $logo_height = imagesy($logo); //logo图片高度
  63. $logo_qr_width = $QR_width / 4; //组合之后logo的宽度(占二维码的1/5)
  64. $scale = $logo_width/$logo_qr_width; //logo的宽度缩放比(本身宽度/组合后的宽度)
  65. $logo_qr_height = $logo_height/$scale; //组合之后logo的高度
  66. $from_width = ($QR_width - $logo_qr_width) / 2; //组合之后logo左上角所在坐标点
  67. //重新组合图片并调整大小
  68. //imagecopyresampled() 将一幅图像(源图象)中的一块正方形区域拷贝到另一个图像中
  69. imagecopyresampled($QR, $logo, $from_width, $from_width, 0, 0, $logo_qr_width,$logo_qr_height, $logo_width, $logo_height);
  70. //输出图片
  71. imagejpeg($QR,$Qrfile);
  72. imagedestroy($QR);
  73. imagedestroy($logo);
  74. }
  75. //在二维码中加入文本
  76. function QRCodeTitle($Qrfile,$title=""){
  77. if(!file_exists($Qrfile)){
  78. return false;
  79. }
  80. $place = 40;//边距
  81. $line_height = 30;//行高
  82. $fileContent = file_get_contents($Qrfile);
  83. if(empty($fileContent)){
  84. return false;
  85. }
  86. $QR = imagecreatefromstring($fileContent); //目标图象连接资源。
  87. $QR_width = imagesx($QR); //二维码图片宽度
  88. $QR_height = imagesy($QR); //二维码图片高度
  89. $Img_width = $QR_width;//画布宽度
  90. $Img_height = $QR_height + $place*2 + $line_height + 20;//画布高度
  91. $Img = imagecreatetruecolor($QR_width,$QR_height + $place*2 + $line_height);
  92. //定义可能用到的颜色
  93. $black=imagecolorallocate($Img,3,3,3);
  94. $white=imagecolorallocate($Img,255,255,255);
  95. //用白色填充画布
  96. imagefill ($Img,0,0,$white);
  97. //将二维码写入画布
  98. imagecopyresampled($Img, $QR, 0, 0, 0, 0, $QR_width, $QR_height, $QR_width, $QR_height);
  99. $pos = [
  100. 'top' => $QR_height+ $place + 20,//顶部边距
  101. 'left' => 0,//左边距
  102. 'line_height'=> $line_height,//行高
  103. 'width' => $QR_width,//段落宽度
  104. 'pos' => 'C'//对齐方式
  105. ];
  106. //定义字体
  107. // $font = PathDS(public_path().'static'.DS.'font'.DS.'zh.ttf');
  108. $font = PathDS(public_path().'static'.DS.'font'.DS.'MSYH.TTC');
  109. $font = file_exists($font)?$font:__DIR__.DS.'MSYH.TTC';
  110. // $font = PathDS(public_path().'static'.DS.'font'.DS.'simfang.ttf');
  111. $txt = [];
  112. $strNum = 0;
  113. for($i=0;$i<mb_strlen($title,"UTF-8");$i++){
  114. $f = mb_substr($title,$i,1,"UTF-8");
  115. if(preg_match('/[\x{4e00}-\x{9fa5}]/u',$f)>0){
  116. $strNum+=1.5;//中文宽度是1.5倍字号
  117. }else{
  118. $strNum+=1;
  119. }
  120. }
  121. $fontSize = intval(($QR_width)/$strNum);//字号,里面的mb_strlen中文需要乘1.5,英文和数字不用
  122. $fontSize = $fontSize<=60?$fontSize:60;
  123. $txt[] = [
  124. 'content' => $title,//文本内容
  125. 'font' => $font,//字体
  126. 'color' => $black,//颜色
  127. 'fontsize' => $fontSize,// 字号
  128. 'blod' => false,//加粗
  129. 'underline' => false//下划线
  130. ];
  131. draw_txt_to($Img,$pos,$txt);
  132. ImageJpeg($Img,$Qrfile,80);
  133. ImageDestroy($Img);
  134. }
  135. /**
  136. * 文字自动换行算法
  137. * @param $card 画板
  138. * @param $pos 段落样式(数组),top顶端的边距,left左边距,line_height行高,widt段落宽度,pos对齐方式:L=左,C=居中,R=右
  139. * @param $txtArr 文本样式(数组),content文本内容,font字体文件,color颜色,fontsize字号,blod是否加粗,underline是否加下划线
  140. * @return int 返回段落底部距离画板顶部的高度
  141. */
  142. function draw_txt_to($card, $pos,$txtArr)
  143. {
  144. //取出段落样式
  145. $_str_h = $pos["top"];
  146. $width = $pos["width"];
  147. $margin_left = $pos["left"];
  148. $hang_size = $pos["line_height"];
  149. $pos = $pos['pos'];
  150. $tp = 0;//行标
  151. $allline = [];//整理后的行数组
  152. $all_temp_string = "";
  153. foreach($txtArr as $key => $sentence){
  154. $fontsize = $sentence["fontsize"];
  155. $fontcolor = $sentence['color'];
  156. $str = $sentence['content'];
  157. $font_file = $sentence['font']&&file_exists($sentence['font'])?$sentence['font']:'./MSYH.TTC';//解决缺少字体文件时报错的BUG
  158. $blod = $sentence['blod'];
  159. $underline = $sentence['underline'];
  160. $temp_string = "";
  161. //将文本按宽度分行
  162. for ($i = 0; $i < mb_strlen($str); $i++) {
  163. //计算每个文字写入前的宽度和高度:0=左下角X位置,1=左下角Y位置,2=右下角X位置,3=右下角Y位置,4=右上角X位置,5=右上角Y位置,6=左上角X位置,7=左上角Y位置
  164. $box = imagettfbbox($fontsize, 0, $font_file, $all_temp_string);
  165. //获得文本宽度
  166. $_string_length = $box[2] - $box[0];
  167. //获取下一个字符
  168. $temptext = mb_substr($str, $i, 1);
  169. //计算下一个字符写入后的宽度
  170. $temp = imagettfbbox($fontsize, 0, $font_file, $temptext);
  171. //字符写入后没有超过规定宽度
  172. if ($_string_length + $temp[2] - $temp[0] < $width) {
  173. $temp_string .= mb_substr($str, $i, 1);
  174. $all_temp_string .= mb_substr($str, $i, 1);
  175. //如果是最后一个字符
  176. if ($i == mb_strlen($str) - 1) {
  177. $allline[$tp][] = ['str'=>$temp_string,'top'=>$_str_h,'color'=>$fontcolor,'fontsize'=>$fontsize,'font_file'=>$font_file,'blod'=>$blod,'underline'=>$underline];
  178. }
  179. }
  180. //一行的字数超过了规定宽度,但字符还没写完;
  181. else{
  182. //剩余的开头第一个字;
  183. $texts = mb_substr($str, $i, 1);
  184. //判断剩余的开头第一个字是不是符号;
  185. $isfuhao = preg_match("/[\\\\pP]/u", $texts) ? true : false;
  186. //如果是标点符号,则添加在第一行的结尾
  187. if ($isfuhao) {
  188. $temp_string .= $texts;
  189. $all_temp_string .= $texts;
  190. //判断如果是连续两个字符出现,并且两个都是必须放在句末尾的,单独处理
  191. $f = mb_substr($str, $i + 1, 1);
  192. $fh = preg_match("/[\\\\pP]/u", $f) ? true : false;
  193. if ($fh) {
  194. $temp_string .= $f;
  195. $all_temp_string .= $f;
  196. $i++;
  197. }
  198. } else {
  199. $i--;
  200. }
  201. $tmp_str_len = mb_strlen($temp_string);
  202. $s = mb_substr($temp_string, $tmp_str_len-1, 1);//取剩余字符串最后一位字符
  203. if (is_firstfuhao($s)) {//判断剩余字符串的最后一个字符是不是可以放在前面
  204. //将最后一个字符用“_”代替。指针前移动一位。重新取被替换的字符。
  205. $temp_string=rtrim($temp_string,$s);
  206. $all_temp_string=rtrim($all_temp_string,$s);
  207. $i--;
  208. }
  209. $allline[$tp][] = ['str'=>$temp_string,'top'=>$_str_h,'color'=>$fontcolor,'fontsize'=>$fontsize,'font_file'=>$font_file,'blod'=>$blod,'underline'=>$underline];
  210. //换行
  211. $_str_h += ($hang_size - ($hang_size - $fontsize)/2);//计算整个文字换行后的高度。
  212. $tp++;//行数
  213. //写完了改行,置null该行的临时字符串。
  214. $temp_string = "";
  215. $all_temp_string = "";
  216. }
  217. }
  218. }
  219. foreach($allline as $hang=>$row){
  220. //此行的所有字符串
  221. $rowstr = implode(",",array_column($row,'str'));
  222. $tmp = imagettfbbox($fontsize, 0, $font_file, $rowstr);
  223. $marginleft = $margin_left;
  224. //计算首句左边距
  225. if($pos=='L'){
  226. $marginleft = $margin_left;
  227. }elseif($pos=='R'){
  228. $marginleft = $margin_left + $width - ($tmp[2] - $tmp[0]);
  229. }elseif($pos=='C'){
  230. $marginleft = $margin_left + ($width - ($tmp[2] - $tmp[0])) / 2;
  231. }
  232. foreach($row as $sub){
  233. $tmp = imagettftext($card, $sub['fontsize'], 0, $marginleft, $sub['top'], $sub['color'], $sub['font_file'], $sub['str']);
  234. if($sub['blod']){
  235. imagettftext($card, $sub['fontsize'], 0, $marginleft, $sub['top']+1, $sub['color'], $sub['font_file'], $sub['str']);
  236. imagettftext($card, $sub['fontsize'], 0, $marginleft, $sub['top']-1, $sub['color'], $sub['font_file'], $sub['str']);
  237. // imagettftext($card, $sub['fontsize'], 0, $marginleft+1, $sub['top'], $sub['color'], $sub['font_file'], $sub['str']);
  238. // imagettftext($card, $sub['fontsize'], 0, $marginleft-1, $sub['top'], $sub['color'], $sub['font_file'], $sub['str']);
  239. }
  240. if($sub['underline']){
  241. imagefilledrectangle($card,$tmp[0],$tmp[1]+1,$tmp[2],$tmp[3]+3,$sub['color']);
  242. }
  243. $marginleft += ($tmp[2] - $tmp[0]);
  244. }
  245. }
  246. return $sub['top'] + $hang_size;
  247. }
  248. function is_firstfuhao($str)
  249. {
  250. $fuhaos = array("\\", "“", "'", "<", "《",);
  251. return in_array($str, $fuhaos);
  252. }
  253. ?>