思路:
fpdf生成PDF文件,fpdi加水印
fpdf类库下载
composer命令安装fpdf:composer require setasign/fpdf
composer命令安装fpdi:composer require setasign/fpdi
fpdf中文插件下载地址:http://www.fpdf.org/download/chinese.zip
中文插件下载解压后放setasign/fpdf 下
使用示例(绘制表格):
require_once('../vendor/setasign/fpdf/chinese.php');//引入中文包
$pdf=new PDF_Chinese('P','mm','A4');//A4设置纸张,最大能设置成A3
$pdf -> AddGBFont();
$pdf -> AddPage('L');//我这里使用的横向纸张,直向可以设置为L
$pdf -> SetFont('GB','',14);//设置字体和大小
//给每列设置宽度,可以根据需要设置宽度,一行放不下会自动换行
$pdf->SetWidths([30,30,30,30,30,30]);
//设置表头
$pdf->MBRow(array(
iconv("UTF-8","gbk","序号"),
iconv("UTF-8","gbk","列1"),
iconv("UTF-8","gbk","列2"),
iconv("UTF-8","gbk","列3"),
iconv("UTF-8","gbk","列4"),
iconv("UTF-8","gbk","列5"),
));
$data = [
[
iconv("UTF-8","gbk",'文字1'),
iconv("UTF-8","gbk",'文字2带英文AAAAAAA'),
iconv("UTF-8","gbk",'文字3带符号;,-、'),
iconv("UTF-8","gbk",'文字4带数字12345678909876'),
iconv("UTF-8","gbk",'文字5'),
iconv("UTF-8","gbk",'文字6带英文符号/\[]----')
],
[
iconv("UTF-8","gbk",'文字1'),
iconv("UTF-8","gbk",'文字2带英文AAAAAAA'),
iconv("UTF-8","gbk",'文字3带符号;,-、'),
iconv("UTF-8","gbk",'文字4带数字12345678909876'),
iconv("UTF-8","gbk",'文字5'),
iconv("UTF-8","gbk",'文字6带英文符号/\[]----')
],
];
//逐行写入
foreach($data as $row){
$pdf->MBRow($row);
}
//可以直接输出
$pdf->Output();
//可以保存文件到指定目录下
//$pdf->Output($fileName,'F');
结果如下:
这个过程中会遇到三个问题:
1、写入中文时,如果遇到换行,行数会计算错误,导致一部分字体在表格外;
2、中文和数字一起写入的时候,数字会圆角显示,导致数字之间间距很大;
3、中文、英文、符号一起作为字符串写入时,某种情况下会进入一个死循环挂掉;
解决方案:
第一个问题:
中文包chinese.php 中的MBRow()是写入一行数据,其中调用了一个NbLines()的函数去查询指定宽度下,换几行。这个函数支持中文的时候,计算行数有问题,在这个函数的基础上我又加了一个MBNbLines()计算中文要分的行数。代码如下:
//$w 设置的列宽度
//$txt 传入的字符串
function MBNbLines($w,$txt)
{
//Computes the number of lines a MultiCell of width w will take
$cw = &$this->CurrentFont['cw'];
if($w==0)//如果没有设置宽度,自动计算宽度
$w=$this->w-$this->rMargin-$this->x;
//$this->cMargin cell margin cell边缘
//$FontSize; current font size in user unit
$wmax=($w-2*$this->cMargin)*1000/$this->FontSize;
$s=str_replace("\r",'',$txt);
$nb=strlen($s);
if($nb>0 and $s[$nb-1]=="\n")
$nb--;
$sep=-1;
$i=0;
$j=0;
$l=0;
$nl=1;
while($i<$nb)
{
// Get next character
$c = $s[$i];
// Check if ASCII or MB
$ascii = (ord($c)<128);
if($c=="\n")
{
// Explicit line break
$i++;
$sep = -1;
$j = $i;
$l = 0;
$nl++;
continue;
}
if(!$ascii){
$sep = $i;
$ls = $l;
}elseif($c==' '){
$sep = $i;
$ls = $l;
}
$l += $ascii ? $cw[$c] : 1000;
if($l>$wmax)//超过一行的宽度
{
// Automatic line break
if($sep==-1 || $i==$j)
{
if($i==$j){
$i += $ascii ? 1 : 2;
}
}
else
{
$i = ($s[$sep]==' ') ? $sep+1 : $sep;
}
$sep = -1;
$j = $i;
$l = 0;
$nl++;
}else{//没超过一行的宽度
$i += $ascii ? 1 : 2;
$sep = $i;
}
}
return $nl;
}
第二个问题:
圆角半角的问题是别的博客找的,在我这里适用。
在chinese.php 的AddGBFont方法中加
$GLOBALS['GB_widths'] = array(' '=>480,'!'=>480,'"'=>480,'#'=>480,'$'=>480,'%'=>480,'&'=>480,'\''=>480,'('=>480,')'=>480,'*'=>480,'+'=>480,','=>480,'-'=>480,'.'=>480,'/'=>480,'0'=>480,'1'=>480,'2'=>480,'3'=>480,'4'=>480,'5'=>480,'6'=>480,'7'=>480,'8'=>480,'9'=>480,':'=>480,';'=>480,'<'=>480,'='=>480,'>'=>480,'?'=>480,'@'=>480,'A'=>480,'B'=>480,'C'=>480,'D'=>480,'E'=>480,'F'=>480,'G'=>480,'H'=>480,'I'=>480,'J'=>480,'K'=>480,'L'=>480,'M'=>480,'N'=>480,'O'=>480,'P'=>480,'Q'=>480,'R'=>480,'S'=>480,'T'=>480,'U'=>480,'V'=>480,'W'=>480,'X'=>480,'Y'=>480,'Z'=>480,'['=>480,'\\'=>480,']'=>480,'^'=>480,'_'=>480,'`'=>480,'a'=>480,'b'=>480,'c'=>480,'d'=>480,'e'=>480,'f'=>480,'g'=>480,'h'=>480,'i'=>480,'j'=>480,'k'=>480,'l'=>480,'m'=>480,'n'=>480,'o'=>480,'p'=>480,'q'=>480,'r'=>480,'s'=>480,'t'=>480,'u'=>480,'v'=>480,'w'=>480,'x'=>480,'y'=>480,'z'=>480,'{'=>480,'|'=>480,'}'=>480,'~'=>480);
函数贴出来
function AddGBFont($family='GB', $name='STSongStd-Light-Acro')
{
$GLOBALS['GB_widths'] = array(' '=>480,'!'=>480,'"'=>480,'#'=>480,'$'=>480,'%'=>480,'&'=>480,'\''=>480,'('=>480,')'=>480,'*'=>480,'+'=>480,','=>480,'-'=>480,'.'=>480,'/'=>480,'0'=>480,'1'=>480,'2'=>480,'3'=>480,'4'=>480,'5'=>480,'6'=>480,'7'=>480,'8'=>480,'9'=>480,':'=>480,';'=>480,'<'=>480,'='=>480,'>'=>480,'?'=>480,'@'=>480,'A'=>480,'B'=>480,'C'=>480,'D'=>480,'E'=>480,'F'=>480,'G'=>480,'H'=>480,'I'=>480,'J'=>480,'K'=>480,'L'=>480,'M'=>480,'N'=>480,'O'=>480,'P'=>480,'Q'=>480,'R'=>480,'S'=>480,'T'=>480,'U'=>480,'V'=>480,'W'=>480,'X'=>480,'Y'=>480,'Z'=>480,'['=>480,'\\'=>480,']'=>480,'^'=>480,'_'=>480,'`'=>480,'a'=>480,'b'=>480,'c'=>480,'d'=>480,'e'=>480,'f'=>480,'g'=>480,'h'=>480,'i'=>480,'j'=>480,'k'=>480,'l'=>480,'m'=>480,'n'=>480,'o'=>480,'p'=>480,'q'=>480,'r'=>480,'s'=>480,'t'=>480,'u'=>480,'v'=>480,'w'=>480,'x'=>480,'y'=>480,'z'=>480,'{'=>480,'|'=>480,'}'=>480,'~'=>480);
// Add GB font with proportional Latin
$cw = $GLOBALS['GB_widths'];
$CMap = 'GBKp-EUC-H';
$registry = array('ordering'=>'GB1', 'supplement'=>2);
$this->AddCIDFonts($family,$name,$cw,$CMap,$registry);
}
第三个问题:
中英文和符号放一个字符串中写入的时候,某种情况下会进入一个死循环,知道内存耗尽或者达到指定执行时间才会终止。经过实验,如果字符串的宽度够宽,是不会出现这个问题的,但是涉及到表格内换行,就可能会出现这个问题,经过排查,发现是源码的MBMultiCell()方法中有一处bug,就是写入的字符串没超过一行宽度的时候,$sep没重新赋值,我把修改后的代码贴出来:
function MBMultiCell($w, $h, $txt, $border=0, $align='L', $fill=0)
{
// Multi-byte version of MultiCell()
$cw = &$this->CurrentFont['cw'];
if($w==0)
$w = $this->w-$this->rMargin-$this->x;
$wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
$s = str_replace("\r",'',$txt);
$nb = strlen($s);
if($nb>0 && $s[$nb-1]=="\n")
$nb--;
$b = 0;
if($border)
{
if($border==1)
{
$border = 'LTRB';
$b = 'LRT';
$b2 = 'LR';
}
else
{
$b2 = '';
if(is_int(strpos($border,'L')))
$b2 .= 'L';
if(is_int(strpos($border,'R')))
$b2 .= 'R';
$b = is_int(strpos($border,'T')) ? $b2.'T' : $b2;
}
}
$sep = -1;
$i = 0;
$j = 0;
$l = 0;
$nl = 1;
while($i<$nb)
{
// Get next character
$c = $s[$i];
// Check if ASCII or MB
$ascii = (ord($c)<128);
if($c=="\n")
{
// Explicit line break
$this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
$i++;
$sep = -1;
$j = $i;
$l = 0;
$nl++;
if($border && $nl==2)
$b = $b2;
continue;
}
if(!$ascii)
{
$sep = $i;
$ls = $l;
}
elseif($c==' ')
{
$sep = $i;
$ls = $l;
}
$l += $ascii ? $cw[$c] : 1000;
if($l>$wmax)
{
// Automatic line break
if($sep==-1 || $i==$j)
{
if($i==$j)
$i += $ascii ? 1 : 2;
$this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
}
else
{
$this->Cell($w,$h,substr($s,$j,$sep-$j),$b,2,$align,$fill);
$i = ($s[$sep]==' ') ? $sep+1 : $sep;
}
$sep = -1;
$j = $i;
$l = 0;
$nl++;
if($border && $nl==2)
$b = $b2;
}
else{
$i += $ascii ? 1 : 2;
$sep = $i;//这是我新加的
}
}
我把改后的chinese.php放到百度网盘,有需要的可以去下载
链接: https://pan.baidu.com/s/1iQew0s8i_8XFfXwDujPAfg?pwd=cguq提取码: cguq 复制这段内容后打开百度网盘手机App,操作更方便哦