使用 FPDF 库的表重叠问题
Overlap issue with tables using FPDF library
我正在使用 FPDF 库开发一个 PHP 网络项目,该项目运行良好。我正在进行的任务是在生成的 pdf 页面中插入一个 table。由于我需要以特定方式输出 table(见下图),因此很难使用可以在此处找到的现有脚本:http://www.fpdf.org/fr/script/index.php。我在我的自定义 PDF class.
中使用 link 中提供的 classes 作为特征
我的MySQLtable:
渲染数据的具体方式:
colonne(法语)= column(英语)/ ligne = line
- 使用 "Tables HTML" 脚本(由 Azeem Abbas 编写,参见 http://www.fpdf.org/en/script/script70.php),我已经能够以正确的方式输出 table(参见下图),但可以无法处理边距、宽度和其他 html 属性。
- 使用 "Table with MySQL" 脚本(由 Olivier 编写,参见 http://www.fpdf.org/en/script/script14.php),无法像使用 "Tables HTML" 脚本那样输出数据。我尝试重写 Table 和 Row 函数,但可以得到很好的结果。
- 在这两种情况下,当单元格中的文本很长时,我都会遇到文本重叠问题。我尝试了 MultiCell 功能,它导致细胞堆积。
这是我使用的代码:
关于可以帮助我在 pdf 页面中获得 html table 重叠问题和更改 table html 属性的可能性的库的任何想法?
更新:2019 年 10 月 10 日
我刚刚在 Olivier (http://www.fpdf.org/en/script/script3.php) 编写的以下代码中找到了重叠问题的答案:
<?php
require('fpdf.php');
class PDF_MC_Table extends FPDF
{
var $widths;
var $aligns;
function SetWidths($w)
{
//Set the array of column widths
$this->widths=$w;
}
function SetAligns($a)
{
//Set the array of column alignments
$this->aligns=$a;
}
function Row($data)
{
//Calculate the height of the row
$nb=0;
for($i=0;$i<count($data);$i++)
$nb=max($nb,$this->NbLines($this->widths[$i],$data[$i]));
$h=5*$nb;
//Issue a page break first if needed
$this->CheckPageBreak($h);
//Draw the cells of the row
for($i=0;$i<count($data);$i++)
{
$w=$this->widths[$i];
$a=isset($this->aligns[$i]) ? $this->aligns[$i] : 'L';
//Save the current position
$x=$this->GetX();
$y=$this->GetY();
//Draw the border
$this->Rect($x,$y,$w,$h);
//Print the text
$this->MultiCell($w,5,$data[$i],0,$a);
//Put the position to the right of the cell
$this->SetXY($x+$w,$y);
}
//Go to the next line
$this->Ln($h);
}
function CheckPageBreak($h)
{
//If the height h would cause an overflow, add a new page immediately
if($this->GetY()+$h>$this->PageBreakTrigger)
$this->AddPage($this->CurOrientation);
}
function NbLines($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;
$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)
{
$c=$s[$i];
if($c=="\n")
{
$i++;
$sep=-1;
$j=$i;
$l=0;
$nl++;
continue;
}
if($c==' ')
$sep=$i;
$l+=$cw[$c];
if($l>$wmax)
{
if($sep==-1)
{
if($i==$j)
$i++;
}
else
$i=$sep+1;
$sep=-1;
$j=$i;
$l=0;
$nl++;
}
else
$i++;
}
return $nl;
}
}
?>
现在我遇到的唯一问题是在上面的代码中编辑 Row()
函数,以便 table 以我提到的特定方式呈现。
这就是 Row()
函数的用法,如上述 class 的文档所述(参见 http://www.fpdf.org/fr/script/script3.php):
function GenerateWord()
{
//Get a random word
$nb=rand(3,10);
$w='';
for($i=1;$i<=$nb;$i++)
$w.=chr(rand(ord('a'),ord('z')));
return $w;
}
function GenerateSentence()
{
//Get a random sentence
$nb=rand(1,10);
$s='';
for($i=1;$i<=$nb;$i++)
$s.=GenerateWord().' ';
return substr($s,0,-1);
}
$pdf=new PDF_MC_Table();
$pdf->AddPage();
$pdf->SetFont('Arial','',14);
//Table with 20 rows and 4 columns
$pdf->SetWidths(array(30,50,30,40));
srand(microtime()*1000000);
for($i=0;$i<20;$i++)
$pdf->Row(array(GenerateSentence(),GenerateSentence(),GenerateSentence(),GenerateSentence()));
$pdf->Output();
在我的例子中,传递给 Row 函数的数组是一个三维数组:
$table =
Array
(
[1] => Array
(
[1] => Array
(
[1] => colonne1 ligne1
)
[2] => Array
(
[4] => colonne2 ligne1
)
[3] => Array
(
[9] => colonne3 ligne1
)
)
[pos_paragr_prec] => 0
[2] => Array
(
[1] => Array
(
[2] => colonne1 ligne2
)
[2] => Array
(
[7] => colonne2 ligne2
)
[3] => Array
(
[10] => colonne3 ligne2
)
)
[3] => Array
(
[1] => Array
(
[3] => colonne1 ligne3
)
[2] => Array
(
[8] => colonne2 ligne3
)
[3] => Array
(
[11] => colonne3 ligne3
)
)
)
关于如何编辑 Row 函数以按上述方式生成 table 的任何提示?
到目前为止,我已经尝试在不编辑 Row 函数的情况下执行类似的操作:
if (count($table) > 0) {
foreach($table as $row_array) {
foreach($row_array as $key => $val) {
for($i=0;$i<count($row_array);$i++) {
$pdf->Row($val);
}
}
}
}
这显然行不通。
更新
我通过这种方式对我的代码进行了一些改进:
if (count($table) > 0) {
if (is_array($table_data) && count($table_data) > 0) {
foreach ($table_data as $key => $value) {
$arrayWidths = [];
foreach($value as $key2 => $val) {
array_push($arrayWidths, 40);
$pdf->SetWidths($arrayWidths);
foreach($val as $k => $col) {
$w=40;//$pdf->widths[$key2];
$a='L';//isset($pdf->aligns[$i]) ? $pdf->aligns[$key2] : 'L';
//Save current position
$x=$pdf->GetX();
$y=$pdf->GetY();
//Drawing the border
$pdf->Rect($x,$y,$w,$h);
//Printing text
$pdf->MultiCell($w,5,$table_data[$key][$key2][$k],0,$a);
//Putting the position to the right of the cell
$pdf->SetXY($x+$w,$y);
}
}
$pdf->Ln();
}
}
}
现在数据正在以我想要的特定方式呈现。但是,第一行和其余行之间存在差异。第一行是acceptable。第一行下方的那些行存在重叠问题。这是我得到的结果:
我使用了这个脚本的逻辑:http://www.fpdf.org/en/script/script3.php(参见上面粘贴的代码)。
有什么想法吗?提前致谢
我已经能够通过重写此处提供的代码以正确的方式生成 table,因此它符合我的上下文的特殊性:fpdf.org/en/script/script14.php .以下是我所做的。
我创建了以下特征,我在 PDF class 中使用了它:
<?php
trait PDF_SQL_Table
{
protected $aCols=array();
protected $TableX;
protected $HeaderColor;
protected $RowColors;
protected $ColorIndex;
/**
* @return void
*/
function TableHeader()
{
$this->SetFont('Arial','B',10);
$this->SetTextColor(0); ## Couleur de police de caractères de l'en-tête du tableau
## Couleur de fond de l'en-tête du tableau
$fill=!empty($this->HeaderColor);
if($fill)
$this->SetFillColor($this->HeaderColor[0],$this->HeaderColor[1],$this->HeaderColor[2]);
}
/**
* @param array $tableData
* @param $width
* @param $align
* @param $TableWidth
* @param $key
* @return float|int
*/
function CalcWidths(array $tableData, $width, $align, $TableWidth, $key)
{
$w = $this->aCols[$key]['w'];
if($w==-1)
$w = $width/count($tableData);
elseif(substr($w,-1) == '%')
$w = $w/100 * $width;
$this->aCols[$key]['w'] = $w;
$TableWidth += $w;
// Calcul de l'abscisse du tableau
if($align=='C')
$this->TableX=max(($this->w-$TableWidth)/2,0);
elseif($align=='R')
$this->TableX= $this->TableX=max($this->w-$this->rMargin-$TableWidth,0);//max($this->w-15-$TableWidth,0); //$this->TableX=max($this->w-$this->rMargin-$TableWidth,0);
else
$this->TableX=$this->lMargin;//15; // $this->TableX=$this->lMargin;
return $w;
}
/**
* @param int $field
* @param int $width
* @param string $caption
* @param string $align
*/
function AddCol($field=-1, $width=-1, $caption='', $align='L'): void
{
// Ajout d'une colonne du tableau
if($field == -1)
$field = count($this->aCols);
$this->aCols[$field]=array('f'=>$field,'c'=>$caption,'w'=>$width,'a'=>$align);
}
/**
* @param array $table
* @param array $prop
* @param array $array_paragraphes
* @param float $interligne
* @param array $excludedParagraphs
* @param $dec
*/
function TableAndParagraphs(array $table = array(), array $prop=array(), array $array_paragraphes, float $interligne, array $excludedParagraphs, $dec): void
{
foreach ($array_paragraphes as $ordre => $paragraphe) {
### $this->Ln(10); En activant ceci à cet endroit précis, un des tableaux s'affiche sur plusieurs pages de façon discontinue
### Insertion d'un tableau au dessous du paragraphe correspondant à ordre_texte_saisi = $table['pos_paragr_prec']
### Utilisation d'une extension de la librairie FPDF disponible ici (que nous avons personnalisée) : http://www.fpdf.org/?go=script&id=14
$counter = 0;
foreach ($table as $num_tableau => $donnees_tableau) {
## Insertion d'un tableau au dessous du paragraphe correspondant à ordre_texte_saisi = $table['pos_paragr_prec']
## Utilisation d'une extension de la librairie FPDF disponible ici : http://www.fpdf.org/?go=script&id=14
// Gestion des propriétés
if(!isset($prop['width']))
$prop['width']=0;
if($prop['width']==0)
$prop['width']= $this->w-$this->lMargin-$this->rMargin;//$this->w-15-15; //($this->lMargin = 15 / $this->rMargin = 15 cf. plus haut)
if(!isset($prop['align']))
$prop['align']='C';
if(!isset($prop['padding']))
$prop['padding']=$this->cMargin;
$cMargin=$this->cMargin;
$this->cMargin=$prop['padding'];
if(!isset($prop['HeaderColor']))
$prop['HeaderColor']=array();
$this->HeaderColor=$prop['HeaderColor'];
if(!isset($prop['color1']))
$prop['color1']=array();
if(!isset($prop['color2']))
$prop['color2']=array();
$this->RowColors=array($prop['color1'],$prop['color2']);
$this->ColorIndex = 0;
if ($ordre === ($table[$num_tableau]['pos_paragr_prec'])) {
$excludedParagraphs[] = $ordre;
### $this->Ln(10); En activant ceci un des tableaux s'affiche sur plusieurs pages de façon discontinue
$paragraphe = html_entity_decode($paragraphe, ENT_HTML5, "UTF-8");
$this->MultiCell(0,$interligne,$paragraphe);
### $this->Ln($dec); En activant ceci, à partir de la 2ème ligne du tableau l'affichage se fait sur plusieurs pages avec une cellule par page
if (count($table) > 0) {
$this->Ln(10); ### Un espace vertical avant d'afficher le tableau
if (is_array($donnees_tableau) && count($donnees_tableau) > 0) {
$iteration = 0; ### indice d'itération sur les tableaux
$TableWidth=0;
foreach ($donnees_tableau as $key => $value) { ### Début d'affichage d'un tableau
### Si la hauteur h = 20 provoque un débordement, on effectue un saut de page manuel
### $this->Ln(0); juste un test : activer ceci donne une idée sur le traitement ligne par ligne (ceci produit une nette séparation entre les lignes)
$this->CheckPageBreak(10);// ceci permet de pouvoir afficher un tableau sur 2 ou plusieurs pages
$this->SetX(15);// On décale le tableau vers la droite de 18 unités (en argument de la fonction SetX()). Permet de déplacer le tableau horizontalement
$number = 0; // Itération sur les données du tableau
foreach($value as $key2 => $val) {
foreach($val as $k => $col) {
// Ajout dynamique d'une colonne au tableau
$this->AddCol($key2, -1, '', 'R');
// Calcul dynamique de la largeur de la colonne
$w = $this->CalcWidths($value, $prop['width'], 'L', $TableWidth, $key2);
$ci = $this->ColorIndex;
$fill=!empty($this->RowColors[$ci]);
if($fill)
$this->SetFillColor($this->RowColors[$ci][0],$this->RowColors[$ci][1],$this->RowColors[$ci][2]);
$this->SetFont('Arial','',9); // Police de caractère pour les tableaux
if (is_numeric($donnees_tableau[$key][$key2][$k])) {
$cellData = number_format($donnees_tableau[$key][$key2][$k], 2, ',',' ');
$alignRight = true;
} elseif (substr($donnees_tableau[$key][$key2][$k], -1) == '%') {
$cellData = number_format($donnees_tableau[$key][$key2][$k], 2, ',',' ').'%';
$alignRight = true;
} elseif (preg_match('/^[0-9+-]{1,3}(\s|,)[0-9]{1,3}\s?[0-9]{0,3},?[0-9]{0,2}/', $donnees_tableau[$key][$key2][$k])) {
$cellData = $donnees_tableau[$key][$key2][$k];
$alignRight = true;
} elseif (preg_match('/[0-9]{1,3},[0-9]{3}\.?[0-9]{0,2}/', $donnees_tableau[$key][$key2][$k])) {
$cellData = str_replace(',', ' ', $donnees_tableau[$key][$key2][$k]);
$cellData = str_replace('.', ',', $cellData);
$alignRight = true;
} elseif (preg_match('/^([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{2,4})$/', $donnees_tableau[$key][$key2][$k])) {
//$cellData = preg_replace('/^([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{2,4})$/', "//", $donnees_tableau[$key][$key2][$k]);
$cellData = date_create($donnees_tableau[$key][$key2][$k]);
$cellData = date_format($cellData, "d/m/Y");
$alignRight = false;
} else {
$cellData = $donnees_tableau[$key][$key2][$k];
$alignRight = false;
}
$x=$this->GetX();
$y=$this->GetY();
## On évite qu'il y ait des Multicells de hauteur inférieure à celle du Cell suivant sur la même ligne
if ($this->GetStringWidth($donnees_tableau[$key][$key2][$k]) < $w && $key2 == '0' /*&& $key >= '1'*/) {
$spaces = "\n"." "; // Espace insécable : " "
$cellData .= $spaces;
}
## Mise en forme du header (police de caractère et couleur de l'arrière-plan)
if ($key == '0') {
$this->TableHeader();
} else {
$this->SetTextColor(0);
}
$this->SetDrawColor(0); ### couleur de la bordure des cellule du tableau : valeur de 0 (noir) à 255 (blanc)
if ($this->GetStringWidth($donnees_tableau[$key][$key2][$k]) > $w || $key2 === 'A' || $key2 == '0') {
$this->MultiCell($w,5,$cellData,1,'L',$fill);
$newY = $this->GetY();
$this->SetXY($x + $w, $y);
} else {
$H = $newY - $y;
if ($alignRight) {
$this->Cell($w, $H,$cellData,1,1,'R',$fill);
} else {
$this->Cell($w, $H,$cellData,1,1,'C',$fill);
}
}
$this->SetFont('Arial','',11);
$this->SetDrawColor(0);
$this->SetXY($x+$w,$y);
}
$number++;
}// affichage d'une ligne entière
if ($iteration == 1) {
$this->Ln(0); ###### 3 : valeur représentant l'écart entre le header du tableau et le corps du tableau
} else {
$this->Ln($H);
}
// Retour à la ligne afin d'afficher la ligne suivante du tableau
$this->ColorIndex = 1-$ci;
$iteration++;
}// Un tableau entier est affiché
}
}
}//if ($ordre === ($table[$num_tableau]['pos_paragr_prec']))
//Espace entre le premier tableau et le paragraphe en dessous
if ($counter == 0) {
$this->Ln(5.5);
}
//Empêcher qu'un paragraphe ne s'affiche plusieurs fois
if ($counter == count($table) - 1) {
//Les paragraphes liés à des tableaux ont été déjà affichés
if (!in_array($ordre, $excludedParagraphs)) {
$this->Ln(5.5);// Espace entre un tableau et le paragraphe en dessous (premier tableau exclus)
$paragraphe = html_entity_decode($paragraphe, ENT_HTML5, "UTF-8");
$this->MultiCell(0,$interligne,$paragraphe);
$this->Ln($dec);
}
}
$counter++;
}
}
}
}
PDF class:
class PDF extends TFPDF
{
protected $B = 0;
protected $I = 0;
protected $U = 0;
protected $HREF = '';
use PDF_MC_Table, PDF_SQL_Table;
function Entete() {
}
function Footer() {
//some code
}
//etc.
}
在控制器中,我可以这样调用 TableAndParagraphs() 函数:
$pdf->TableAndParagraphs($table, $prop, $array_paragraphes, $interligne, $excludedParagraphs, $dec);
注意TFPDF是基于FPDF的库(网上可以找到):
<?php
/*******************************************************************************
* tFPDF (based on FPDF 1.7) *
* *
* Version: 1.24 *
* Date: 2011-09-24 *
* Author: Ian Back <ianb@bpm1.com> *
* License: LGPL *
*******************************************************************************/
define('tFPDF_VERSION','1.24');
class tFPDF
{
var $unifontSubset;
var $page; // current page number
var $n; // current object number
var $offsets; // array of object offsets
var $buffer; // buffer holding in-memory PDF
var $pages; // array containing pages
var $state; // current document state
var $compress; // compression flag
var $k; // scale factor (number of points in user unit)
var $DefOrientation; // default orientation
var $CurOrientation; // current orientation
var $StdPageSizes; // standard page sizes
var $DefPageSize; // default page size
var $CurPageSize; // current page size
var $PageSizes; // used for pages with non default sizes or orientations
var $wPt, $hPt; // dimensions of current page in points
var $w, $h; // dimensions of current page in user unit
var $lMargin; // left margin
var $tMargin; // top margin
var $rMargin; // right margin
var $bMargin; // page break margin
var $cMargin; // cell margin
var $x, $y; // current position in user unit
var $lasth; // height of last printed cell
var $LineWidth; // line width in user unit
var $fontpath; // path containing fonts
var $CoreFonts; // array of core font names
var $fonts; // array of used fonts
var $FontFiles; // array of font files
var $diffs; // array of encoding differences
var $FontFamily; // current font family
var $FontStyle; // current font style
var $underline; // underlining flag
var $CurrentFont; // current font info
var $FontSizePt; // current font size in points
var $FontSize; // current font size in user unit
var $DrawColor; // commands for drawing color
var $FillColor; // commands for filling color
var $TextColor; // commands for text color
var $ColorFlag; // indicates whether fill and text colors are different
var $ws; // word spacing
var $images; // array of used images
var $PageLinks; // array of links in pages
var $links; // array of internal links
var $AutoPageBreak; // automatic page breaking
var $PageBreakTrigger; // threshold used to trigger page breaks
var $InHeader; // flag set when processing header
var $InFooter; // flag set when processing footer
var $ZoomMode; // zoom display mode
var $LayoutMode; // layout display mode
var $title; // title
var $subject; // subject
var $author; // author
var $keywords; // keywords
var $creator; // creator
var $AliasNbPages; // alias for total number of pages
var $PDFVersion; // PDF version number
/*******************************************************************************
* *
* Public methods *
* *
*******************************************************************************/
function __construct($orientation='P', $unit='mm', $size='A4')
{
// Some checks
$this->_dochecks();
// Initialization of properties
$this->page = 0;
$this->n = 2;
$this->buffer = '';
$this->pages = array();
$this->PageSizes = array();
$this->state = 0;
$this->fonts = array();
$this->FontFiles = array();
$this->diffs = array();
$this->images = array();
$this->links = array();
$this->InHeader = false;
$this->InFooter = false;
$this->lasth = 0;
$this->FontFamily = '';
$this->FontStyle = '';
$this->FontSizePt = 12;
$this->underline = false;
$this->DrawColor = '0 G';
$this->FillColor = '0 g';
$this->TextColor = '0 g';
$this->ColorFlag = false;
$this->ws = 0;
// Font path
if(defined('FPDF_FONTPATH'))
{
$this->fontpath = FPDF_FONTPATH;
if(substr($this->fontpath,-1)!='/' && substr($this->fontpath,-1)!='\')
$this->fontpath .= '/';
}
elseif(is_dir(dirname(__FILE__).'/font'))
$this->fontpath = dirname(__FILE__).'/font/';
else
$this->fontpath = '';
// Core fonts
$this->CoreFonts = array('courier', 'helvetica', 'times', 'symbol', 'zapfdingbats');
// Scale factor
if($unit=='pt')
$this->k = 1;
elseif($unit=='mm')
$this->k = 72/25.4;
elseif($unit=='cm')
$this->k = 72/2.54;
elseif($unit=='in')
$this->k = 72;
else
$this->Error('Incorrect unit: '.$unit);
// Page sizes
$this->StdPageSizes = array('a3'=>array(841.89,1190.55), 'a4'=>array(595.28,841.89), 'a5'=>array(420.94,595.28),
'letter'=>array(612,792), 'legal'=>array(612,1008));
$size = $this->_getpagesize($size);
$this->DefPageSize = $size;
$this->CurPageSize = $size;
// Page orientation
$orientation = mb_strtolower($orientation); // 20160707 ajout mb_
if($orientation=='p' || $orientation=='portrait')
{
$this->DefOrientation = 'P';
$this->w = $size[0];
$this->h = $size[1];
}
elseif($orientation=='l' || $orientation=='landscape')
{
$this->DefOrientation = 'L';
$this->w = $size[1];
$this->h = $size[0];
}
else
$this->Error('Incorrect orientation: '.$orientation);
$this->CurOrientation = $this->DefOrientation;
$this->wPt = $this->w*$this->k;
$this->hPt = $this->h*$this->k;
// Page margins (1 cm)
$margin = 28.35/$this->k;
$this->SetMargins($margin,$margin);
// Interior cell margin (1 mm)
$this->cMargin = $margin/10;
// Line width (0.2 mm)
$this->LineWidth = .567/$this->k;
// Automatic page break
$this->SetAutoPageBreak(true,2*$margin);
// Default display mode
$this->SetDisplayMode('default');
// Enable compression
$this->SetCompression(true);
// Set default PDF version number
$this->PDFVersion = '1.3';
}
//etc.
}
以防万一您将 TFPDF 与 PDF_MC_Table 特征(参见 Patrick 的回答中的示例 2)和 UTF-8 编码结合使用,那么您需要替换原始的 NbLines() 函数在下面。基本上它是 TFPDF MultiCell() 函数的副本,其中删除了输出行。
public function NbLines($w,$txt) {
$unifontSubset = property_exists($this, 'unifontSubset') && $this->unifontSubset; # compatible with FPDF and TFPDF.
// Output text with automatic or explicit line breaks
if(!isset($this->CurrentFont))
$this->Error('No font has been set');
$cw = &$this->CurrentFont['cw'];
if($w==0)
$w = $this->w-$this->rMargin-$this->x;
$wmax = ($w-2*$this->cMargin);
//$wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
$s = str_replace("\r",'',(string)$txt);
if ($unifontSubset) {
$nb=mb_strlen($s, 'utf-8');
while($nb>0 && mb_substr($s,$nb-1,1,'utf-8')=="\n") $nb--;
}
else {
$nb = strlen($s);
if($nb>0 && substr($s, $nb-1, 1) == "\n")
$nb--;
}
$sep = -1;
$i = 0;
$j = 0;
$l = 0;
$ns = 0;
$nl = 1;
while($i<$nb)
{
// Get next character
if ($unifontSubset) {
$c = mb_substr($s,$i,1,'UTF-8');
}
else {
$c = substr($s,$i,1);
}
if($c=="\n")
{
$i++;
$sep = -1;
$j = $i;
$l = 0;
$ns = 0;
$nl++;
continue;
}
if($c==' ')
{
$sep = $i;
$ls = $l;
$ns++;
}
if ($unifontSubset) { $l += $this->GetStringWidth($c); }
else { $l += $cw[$c]*$this->FontSize/1000; }
if($l>$wmax)
{
// Automatic line break
if($sep==-1)
{
if($i==$j)
$i++;
}
else
{
$i = $sep+1;
}
$sep = -1;
$j = $i;
$l = 0;
$ns = 0;
$nl++;
}
else
$i++;
}
return $nl;
}
我正在使用 FPDF 库开发一个 PHP 网络项目,该项目运行良好。我正在进行的任务是在生成的 pdf 页面中插入一个 table。由于我需要以特定方式输出 table(见下图),因此很难使用可以在此处找到的现有脚本:http://www.fpdf.org/fr/script/index.php。我在我的自定义 PDF class.
中使用 link 中提供的 classes 作为特征我的MySQLtable:
渲染数据的具体方式:
colonne(法语)= column(英语)/ ligne = line
- 使用 "Tables HTML" 脚本(由 Azeem Abbas 编写,参见 http://www.fpdf.org/en/script/script70.php),我已经能够以正确的方式输出 table(参见下图),但可以无法处理边距、宽度和其他 html 属性。
- 使用 "Table with MySQL" 脚本(由 Olivier 编写,参见 http://www.fpdf.org/en/script/script14.php),无法像使用 "Tables HTML" 脚本那样输出数据。我尝试重写 Table 和 Row 函数,但可以得到很好的结果。
- 在这两种情况下,当单元格中的文本很长时,我都会遇到文本重叠问题。我尝试了 MultiCell 功能,它导致细胞堆积。
这是我使用的代码:
关于可以帮助我在 pdf 页面中获得 html table 重叠问题和更改 table html 属性的可能性的库的任何想法?
更新:2019 年 10 月 10 日 我刚刚在 Olivier (http://www.fpdf.org/en/script/script3.php) 编写的以下代码中找到了重叠问题的答案:
<?php
require('fpdf.php');
class PDF_MC_Table extends FPDF
{
var $widths;
var $aligns;
function SetWidths($w)
{
//Set the array of column widths
$this->widths=$w;
}
function SetAligns($a)
{
//Set the array of column alignments
$this->aligns=$a;
}
function Row($data)
{
//Calculate the height of the row
$nb=0;
for($i=0;$i<count($data);$i++)
$nb=max($nb,$this->NbLines($this->widths[$i],$data[$i]));
$h=5*$nb;
//Issue a page break first if needed
$this->CheckPageBreak($h);
//Draw the cells of the row
for($i=0;$i<count($data);$i++)
{
$w=$this->widths[$i];
$a=isset($this->aligns[$i]) ? $this->aligns[$i] : 'L';
//Save the current position
$x=$this->GetX();
$y=$this->GetY();
//Draw the border
$this->Rect($x,$y,$w,$h);
//Print the text
$this->MultiCell($w,5,$data[$i],0,$a);
//Put the position to the right of the cell
$this->SetXY($x+$w,$y);
}
//Go to the next line
$this->Ln($h);
}
function CheckPageBreak($h)
{
//If the height h would cause an overflow, add a new page immediately
if($this->GetY()+$h>$this->PageBreakTrigger)
$this->AddPage($this->CurOrientation);
}
function NbLines($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;
$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)
{
$c=$s[$i];
if($c=="\n")
{
$i++;
$sep=-1;
$j=$i;
$l=0;
$nl++;
continue;
}
if($c==' ')
$sep=$i;
$l+=$cw[$c];
if($l>$wmax)
{
if($sep==-1)
{
if($i==$j)
$i++;
}
else
$i=$sep+1;
$sep=-1;
$j=$i;
$l=0;
$nl++;
}
else
$i++;
}
return $nl;
}
}
?>
现在我遇到的唯一问题是在上面的代码中编辑 Row()
函数,以便 table 以我提到的特定方式呈现。
这就是 Row()
函数的用法,如上述 class 的文档所述(参见 http://www.fpdf.org/fr/script/script3.php):
function GenerateWord()
{
//Get a random word
$nb=rand(3,10);
$w='';
for($i=1;$i<=$nb;$i++)
$w.=chr(rand(ord('a'),ord('z')));
return $w;
}
function GenerateSentence()
{
//Get a random sentence
$nb=rand(1,10);
$s='';
for($i=1;$i<=$nb;$i++)
$s.=GenerateWord().' ';
return substr($s,0,-1);
}
$pdf=new PDF_MC_Table();
$pdf->AddPage();
$pdf->SetFont('Arial','',14);
//Table with 20 rows and 4 columns
$pdf->SetWidths(array(30,50,30,40));
srand(microtime()*1000000);
for($i=0;$i<20;$i++)
$pdf->Row(array(GenerateSentence(),GenerateSentence(),GenerateSentence(),GenerateSentence()));
$pdf->Output();
在我的例子中,传递给 Row 函数的数组是一个三维数组:
$table =
Array
(
[1] => Array
(
[1] => Array
(
[1] => colonne1 ligne1
)
[2] => Array
(
[4] => colonne2 ligne1
)
[3] => Array
(
[9] => colonne3 ligne1
)
)
[pos_paragr_prec] => 0
[2] => Array
(
[1] => Array
(
[2] => colonne1 ligne2
)
[2] => Array
(
[7] => colonne2 ligne2
)
[3] => Array
(
[10] => colonne3 ligne2
)
)
[3] => Array
(
[1] => Array
(
[3] => colonne1 ligne3
)
[2] => Array
(
[8] => colonne2 ligne3
)
[3] => Array
(
[11] => colonne3 ligne3
)
)
)
关于如何编辑 Row 函数以按上述方式生成 table 的任何提示?
到目前为止,我已经尝试在不编辑 Row 函数的情况下执行类似的操作:
if (count($table) > 0) {
foreach($table as $row_array) {
foreach($row_array as $key => $val) {
for($i=0;$i<count($row_array);$i++) {
$pdf->Row($val);
}
}
}
}
这显然行不通。
更新
我通过这种方式对我的代码进行了一些改进:
if (count($table) > 0) {
if (is_array($table_data) && count($table_data) > 0) {
foreach ($table_data as $key => $value) {
$arrayWidths = [];
foreach($value as $key2 => $val) {
array_push($arrayWidths, 40);
$pdf->SetWidths($arrayWidths);
foreach($val as $k => $col) {
$w=40;//$pdf->widths[$key2];
$a='L';//isset($pdf->aligns[$i]) ? $pdf->aligns[$key2] : 'L';
//Save current position
$x=$pdf->GetX();
$y=$pdf->GetY();
//Drawing the border
$pdf->Rect($x,$y,$w,$h);
//Printing text
$pdf->MultiCell($w,5,$table_data[$key][$key2][$k],0,$a);
//Putting the position to the right of the cell
$pdf->SetXY($x+$w,$y);
}
}
$pdf->Ln();
}
}
}
现在数据正在以我想要的特定方式呈现。但是,第一行和其余行之间存在差异。第一行是acceptable。第一行下方的那些行存在重叠问题。这是我得到的结果:
我使用了这个脚本的逻辑:http://www.fpdf.org/en/script/script3.php(参见上面粘贴的代码)。
有什么想法吗?提前致谢
我已经能够通过重写此处提供的代码以正确的方式生成 table,因此它符合我的上下文的特殊性:fpdf.org/en/script/script14.php .以下是我所做的。
我创建了以下特征,我在 PDF class 中使用了它:
<?php trait PDF_SQL_Table { protected $aCols=array(); protected $TableX; protected $HeaderColor; protected $RowColors; protected $ColorIndex; /** * @return void */ function TableHeader() { $this->SetFont('Arial','B',10); $this->SetTextColor(0); ## Couleur de police de caractères de l'en-tête du tableau ## Couleur de fond de l'en-tête du tableau $fill=!empty($this->HeaderColor); if($fill) $this->SetFillColor($this->HeaderColor[0],$this->HeaderColor[1],$this->HeaderColor[2]); } /** * @param array $tableData * @param $width * @param $align * @param $TableWidth * @param $key * @return float|int */ function CalcWidths(array $tableData, $width, $align, $TableWidth, $key) { $w = $this->aCols[$key]['w']; if($w==-1) $w = $width/count($tableData); elseif(substr($w,-1) == '%') $w = $w/100 * $width; $this->aCols[$key]['w'] = $w; $TableWidth += $w; // Calcul de l'abscisse du tableau if($align=='C') $this->TableX=max(($this->w-$TableWidth)/2,0); elseif($align=='R') $this->TableX= $this->TableX=max($this->w-$this->rMargin-$TableWidth,0);//max($this->w-15-$TableWidth,0); //$this->TableX=max($this->w-$this->rMargin-$TableWidth,0); else $this->TableX=$this->lMargin;//15; // $this->TableX=$this->lMargin; return $w; } /** * @param int $field * @param int $width * @param string $caption * @param string $align */ function AddCol($field=-1, $width=-1, $caption='', $align='L'): void { // Ajout d'une colonne du tableau if($field == -1) $field = count($this->aCols); $this->aCols[$field]=array('f'=>$field,'c'=>$caption,'w'=>$width,'a'=>$align); } /** * @param array $table * @param array $prop * @param array $array_paragraphes * @param float $interligne * @param array $excludedParagraphs * @param $dec */ function TableAndParagraphs(array $table = array(), array $prop=array(), array $array_paragraphes, float $interligne, array $excludedParagraphs, $dec): void { foreach ($array_paragraphes as $ordre => $paragraphe) { ### $this->Ln(10); En activant ceci à cet endroit précis, un des tableaux s'affiche sur plusieurs pages de façon discontinue ### Insertion d'un tableau au dessous du paragraphe correspondant à ordre_texte_saisi = $table['pos_paragr_prec'] ### Utilisation d'une extension de la librairie FPDF disponible ici (que nous avons personnalisée) : http://www.fpdf.org/?go=script&id=14 $counter = 0; foreach ($table as $num_tableau => $donnees_tableau) { ## Insertion d'un tableau au dessous du paragraphe correspondant à ordre_texte_saisi = $table['pos_paragr_prec'] ## Utilisation d'une extension de la librairie FPDF disponible ici : http://www.fpdf.org/?go=script&id=14 // Gestion des propriétés if(!isset($prop['width'])) $prop['width']=0; if($prop['width']==0) $prop['width']= $this->w-$this->lMargin-$this->rMargin;//$this->w-15-15; //($this->lMargin = 15 / $this->rMargin = 15 cf. plus haut) if(!isset($prop['align'])) $prop['align']='C'; if(!isset($prop['padding'])) $prop['padding']=$this->cMargin; $cMargin=$this->cMargin; $this->cMargin=$prop['padding']; if(!isset($prop['HeaderColor'])) $prop['HeaderColor']=array(); $this->HeaderColor=$prop['HeaderColor']; if(!isset($prop['color1'])) $prop['color1']=array(); if(!isset($prop['color2'])) $prop['color2']=array(); $this->RowColors=array($prop['color1'],$prop['color2']); $this->ColorIndex = 0; if ($ordre === ($table[$num_tableau]['pos_paragr_prec'])) { $excludedParagraphs[] = $ordre; ### $this->Ln(10); En activant ceci un des tableaux s'affiche sur plusieurs pages de façon discontinue $paragraphe = html_entity_decode($paragraphe, ENT_HTML5, "UTF-8"); $this->MultiCell(0,$interligne,$paragraphe); ### $this->Ln($dec); En activant ceci, à partir de la 2ème ligne du tableau l'affichage se fait sur plusieurs pages avec une cellule par page if (count($table) > 0) { $this->Ln(10); ### Un espace vertical avant d'afficher le tableau if (is_array($donnees_tableau) && count($donnees_tableau) > 0) { $iteration = 0; ### indice d'itération sur les tableaux $TableWidth=0; foreach ($donnees_tableau as $key => $value) { ### Début d'affichage d'un tableau ### Si la hauteur h = 20 provoque un débordement, on effectue un saut de page manuel ### $this->Ln(0); juste un test : activer ceci donne une idée sur le traitement ligne par ligne (ceci produit une nette séparation entre les lignes) $this->CheckPageBreak(10);// ceci permet de pouvoir afficher un tableau sur 2 ou plusieurs pages $this->SetX(15);// On décale le tableau vers la droite de 18 unités (en argument de la fonction SetX()). Permet de déplacer le tableau horizontalement $number = 0; // Itération sur les données du tableau foreach($value as $key2 => $val) { foreach($val as $k => $col) { // Ajout dynamique d'une colonne au tableau $this->AddCol($key2, -1, '', 'R'); // Calcul dynamique de la largeur de la colonne $w = $this->CalcWidths($value, $prop['width'], 'L', $TableWidth, $key2); $ci = $this->ColorIndex; $fill=!empty($this->RowColors[$ci]); if($fill) $this->SetFillColor($this->RowColors[$ci][0],$this->RowColors[$ci][1],$this->RowColors[$ci][2]); $this->SetFont('Arial','',9); // Police de caractère pour les tableaux if (is_numeric($donnees_tableau[$key][$key2][$k])) { $cellData = number_format($donnees_tableau[$key][$key2][$k], 2, ',',' '); $alignRight = true; } elseif (substr($donnees_tableau[$key][$key2][$k], -1) == '%') { $cellData = number_format($donnees_tableau[$key][$key2][$k], 2, ',',' ').'%'; $alignRight = true; } elseif (preg_match('/^[0-9+-]{1,3}(\s|,)[0-9]{1,3}\s?[0-9]{0,3},?[0-9]{0,2}/', $donnees_tableau[$key][$key2][$k])) { $cellData = $donnees_tableau[$key][$key2][$k]; $alignRight = true; } elseif (preg_match('/[0-9]{1,3},[0-9]{3}\.?[0-9]{0,2}/', $donnees_tableau[$key][$key2][$k])) { $cellData = str_replace(',', ' ', $donnees_tableau[$key][$key2][$k]); $cellData = str_replace('.', ',', $cellData); $alignRight = true; } elseif (preg_match('/^([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{2,4})$/', $donnees_tableau[$key][$key2][$k])) { //$cellData = preg_replace('/^([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{2,4})$/', "//", $donnees_tableau[$key][$key2][$k]); $cellData = date_create($donnees_tableau[$key][$key2][$k]); $cellData = date_format($cellData, "d/m/Y"); $alignRight = false; } else { $cellData = $donnees_tableau[$key][$key2][$k]; $alignRight = false; } $x=$this->GetX(); $y=$this->GetY(); ## On évite qu'il y ait des Multicells de hauteur inférieure à celle du Cell suivant sur la même ligne if ($this->GetStringWidth($donnees_tableau[$key][$key2][$k]) < $w && $key2 == '0' /*&& $key >= '1'*/) { $spaces = "\n"." "; // Espace insécable : " " $cellData .= $spaces; } ## Mise en forme du header (police de caractère et couleur de l'arrière-plan) if ($key == '0') { $this->TableHeader(); } else { $this->SetTextColor(0); } $this->SetDrawColor(0); ### couleur de la bordure des cellule du tableau : valeur de 0 (noir) à 255 (blanc) if ($this->GetStringWidth($donnees_tableau[$key][$key2][$k]) > $w || $key2 === 'A' || $key2 == '0') { $this->MultiCell($w,5,$cellData,1,'L',$fill); $newY = $this->GetY(); $this->SetXY($x + $w, $y); } else { $H = $newY - $y; if ($alignRight) { $this->Cell($w, $H,$cellData,1,1,'R',$fill); } else { $this->Cell($w, $H,$cellData,1,1,'C',$fill); } } $this->SetFont('Arial','',11); $this->SetDrawColor(0); $this->SetXY($x+$w,$y); } $number++; }// affichage d'une ligne entière if ($iteration == 1) { $this->Ln(0); ###### 3 : valeur représentant l'écart entre le header du tableau et le corps du tableau } else { $this->Ln($H); } // Retour à la ligne afin d'afficher la ligne suivante du tableau $this->ColorIndex = 1-$ci; $iteration++; }// Un tableau entier est affiché } } }//if ($ordre === ($table[$num_tableau]['pos_paragr_prec'])) //Espace entre le premier tableau et le paragraphe en dessous if ($counter == 0) { $this->Ln(5.5); } //Empêcher qu'un paragraphe ne s'affiche plusieurs fois if ($counter == count($table) - 1) { //Les paragraphes liés à des tableaux ont été déjà affichés if (!in_array($ordre, $excludedParagraphs)) { $this->Ln(5.5);// Espace entre un tableau et le paragraphe en dessous (premier tableau exclus) $paragraphe = html_entity_decode($paragraphe, ENT_HTML5, "UTF-8"); $this->MultiCell(0,$interligne,$paragraphe); $this->Ln($dec); } } $counter++; } } } }
PDF class:
class PDF extends TFPDF { protected $B = 0; protected $I = 0; protected $U = 0; protected $HREF = ''; use PDF_MC_Table, PDF_SQL_Table; function Entete() { } function Footer() { //some code } //etc. }
在控制器中,我可以这样调用 TableAndParagraphs() 函数:
$pdf->TableAndParagraphs($table, $prop, $array_paragraphes, $interligne, $excludedParagraphs, $dec);
注意TFPDF是基于FPDF的库(网上可以找到):
<?php /******************************************************************************* * tFPDF (based on FPDF 1.7) * * * * Version: 1.24 * * Date: 2011-09-24 * * Author: Ian Back <ianb@bpm1.com> * * License: LGPL * *******************************************************************************/ define('tFPDF_VERSION','1.24'); class tFPDF { var $unifontSubset; var $page; // current page number var $n; // current object number var $offsets; // array of object offsets var $buffer; // buffer holding in-memory PDF var $pages; // array containing pages var $state; // current document state var $compress; // compression flag var $k; // scale factor (number of points in user unit) var $DefOrientation; // default orientation var $CurOrientation; // current orientation var $StdPageSizes; // standard page sizes var $DefPageSize; // default page size var $CurPageSize; // current page size var $PageSizes; // used for pages with non default sizes or orientations var $wPt, $hPt; // dimensions of current page in points var $w, $h; // dimensions of current page in user unit var $lMargin; // left margin var $tMargin; // top margin var $rMargin; // right margin var $bMargin; // page break margin var $cMargin; // cell margin var $x, $y; // current position in user unit var $lasth; // height of last printed cell var $LineWidth; // line width in user unit var $fontpath; // path containing fonts var $CoreFonts; // array of core font names var $fonts; // array of used fonts var $FontFiles; // array of font files var $diffs; // array of encoding differences var $FontFamily; // current font family var $FontStyle; // current font style var $underline; // underlining flag var $CurrentFont; // current font info var $FontSizePt; // current font size in points var $FontSize; // current font size in user unit var $DrawColor; // commands for drawing color var $FillColor; // commands for filling color var $TextColor; // commands for text color var $ColorFlag; // indicates whether fill and text colors are different var $ws; // word spacing var $images; // array of used images var $PageLinks; // array of links in pages var $links; // array of internal links var $AutoPageBreak; // automatic page breaking var $PageBreakTrigger; // threshold used to trigger page breaks var $InHeader; // flag set when processing header var $InFooter; // flag set when processing footer var $ZoomMode; // zoom display mode var $LayoutMode; // layout display mode var $title; // title var $subject; // subject var $author; // author var $keywords; // keywords var $creator; // creator var $AliasNbPages; // alias for total number of pages var $PDFVersion; // PDF version number /******************************************************************************* * * * Public methods * * * *******************************************************************************/ function __construct($orientation='P', $unit='mm', $size='A4') { // Some checks $this->_dochecks(); // Initialization of properties $this->page = 0; $this->n = 2; $this->buffer = ''; $this->pages = array(); $this->PageSizes = array(); $this->state = 0; $this->fonts = array(); $this->FontFiles = array(); $this->diffs = array(); $this->images = array(); $this->links = array(); $this->InHeader = false; $this->InFooter = false; $this->lasth = 0; $this->FontFamily = ''; $this->FontStyle = ''; $this->FontSizePt = 12; $this->underline = false; $this->DrawColor = '0 G'; $this->FillColor = '0 g'; $this->TextColor = '0 g'; $this->ColorFlag = false; $this->ws = 0; // Font path if(defined('FPDF_FONTPATH')) { $this->fontpath = FPDF_FONTPATH; if(substr($this->fontpath,-1)!='/' && substr($this->fontpath,-1)!='\') $this->fontpath .= '/'; } elseif(is_dir(dirname(__FILE__).'/font')) $this->fontpath = dirname(__FILE__).'/font/'; else $this->fontpath = ''; // Core fonts $this->CoreFonts = array('courier', 'helvetica', 'times', 'symbol', 'zapfdingbats'); // Scale factor if($unit=='pt') $this->k = 1; elseif($unit=='mm') $this->k = 72/25.4; elseif($unit=='cm') $this->k = 72/2.54; elseif($unit=='in') $this->k = 72; else $this->Error('Incorrect unit: '.$unit); // Page sizes $this->StdPageSizes = array('a3'=>array(841.89,1190.55), 'a4'=>array(595.28,841.89), 'a5'=>array(420.94,595.28), 'letter'=>array(612,792), 'legal'=>array(612,1008)); $size = $this->_getpagesize($size); $this->DefPageSize = $size; $this->CurPageSize = $size; // Page orientation $orientation = mb_strtolower($orientation); // 20160707 ajout mb_ if($orientation=='p' || $orientation=='portrait') { $this->DefOrientation = 'P'; $this->w = $size[0]; $this->h = $size[1]; } elseif($orientation=='l' || $orientation=='landscape') { $this->DefOrientation = 'L'; $this->w = $size[1]; $this->h = $size[0]; } else $this->Error('Incorrect orientation: '.$orientation); $this->CurOrientation = $this->DefOrientation; $this->wPt = $this->w*$this->k; $this->hPt = $this->h*$this->k; // Page margins (1 cm) $margin = 28.35/$this->k; $this->SetMargins($margin,$margin); // Interior cell margin (1 mm) $this->cMargin = $margin/10; // Line width (0.2 mm) $this->LineWidth = .567/$this->k; // Automatic page break $this->SetAutoPageBreak(true,2*$margin); // Default display mode $this->SetDisplayMode('default'); // Enable compression $this->SetCompression(true); // Set default PDF version number $this->PDFVersion = '1.3'; } //etc. }
以防万一您将 TFPDF 与 PDF_MC_Table 特征(参见 Patrick 的回答中的示例 2)和 UTF-8 编码结合使用,那么您需要替换原始的 NbLines() 函数在下面。基本上它是 TFPDF MultiCell() 函数的副本,其中删除了输出行。
public function NbLines($w,$txt) {
$unifontSubset = property_exists($this, 'unifontSubset') && $this->unifontSubset; # compatible with FPDF and TFPDF.
// Output text with automatic or explicit line breaks
if(!isset($this->CurrentFont))
$this->Error('No font has been set');
$cw = &$this->CurrentFont['cw'];
if($w==0)
$w = $this->w-$this->rMargin-$this->x;
$wmax = ($w-2*$this->cMargin);
//$wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
$s = str_replace("\r",'',(string)$txt);
if ($unifontSubset) {
$nb=mb_strlen($s, 'utf-8');
while($nb>0 && mb_substr($s,$nb-1,1,'utf-8')=="\n") $nb--;
}
else {
$nb = strlen($s);
if($nb>0 && substr($s, $nb-1, 1) == "\n")
$nb--;
}
$sep = -1;
$i = 0;
$j = 0;
$l = 0;
$ns = 0;
$nl = 1;
while($i<$nb)
{
// Get next character
if ($unifontSubset) {
$c = mb_substr($s,$i,1,'UTF-8');
}
else {
$c = substr($s,$i,1);
}
if($c=="\n")
{
$i++;
$sep = -1;
$j = $i;
$l = 0;
$ns = 0;
$nl++;
continue;
}
if($c==' ')
{
$sep = $i;
$ls = $l;
$ns++;
}
if ($unifontSubset) { $l += $this->GetStringWidth($c); }
else { $l += $cw[$c]*$this->FontSize/1000; }
if($l>$wmax)
{
// Automatic line break
if($sep==-1)
{
if($i==$j)
$i++;
}
else
{
$i = $sep+1;
}
$sep = -1;
$j = $i;
$l = 0;
$ns = 0;
$nl++;
}
else
$i++;
}
return $nl;
}