PHP 记一次使用fpdf、fpdi生成报表,并加水印导出的过程

思路:

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,操作更方便哦

加水印

加水印没遇见什么难题,参考这位博主的代码写的PHP使用FPDF、Fpdi类库给PDF文件添加水印_凌晨四点的_LA的博客-CSDN博客_composer fpdf/** * PDF文件加水印 * composer命令安装:composer require setasign/fpdf * composer命令安装:composer require setasign/fpdi */public function add_water($type = 1){ $pdf = new Fpdi(); $file = 'Upload/all_...https://blog.csdn.net/weixin_42047371/article/details/99632700

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值