(FPDF, PHP, SQL) table 第一页后的值变得凌乱

(FPDF, PHP, SQL) table values after first page gets messy

我正在使用 PHP 和 fPDF 创建一个 PDF "invoice" 并且在第一页上一切正常,但是当 table 必须转到第二页时它只returns 来自 sql 查询的第一个值,其余的都转到第三页,依此类推。

这是循环 table 行的代码

  $sql=sprintf("SELECT * FROM rostosativos_invoice where id_proposta = '".$_GET['id']."';");
  $res=mysqli_query($link, $sql);
  while ($r=mysqli_fetch_assoc($res)){

        // produto = posição 5
        // quantidade = posição 7
        // precouni = posição 6
        // soma = posição 9

        //multicell 
        $cellWidth=120;//tamanho da cell
        $cellHeight=6.5;//altura da cell

        //verificar se o texto passa a cell
        if($pdf->GetStringWidth($r['produto']) < $cellWidth){
              //se não, não fazer nada
              $line=1;
        }else{
              //~se estiver, ~então calcular a altura necessária para a cobrir a cell
              //ao dividir o texto para ajustar ao tamanho da cell
              //~depois contar quantas linhas são necessãrias para ajustar o texto na cell

              $textLength=strlen($r['produto']); //total text length
              $errMargin=10;          //cell com margem de erro, just in case
              $startChar=0;           //posição inicial para cada linha
              $maxChar=0;             //Máxima caracteres numa linha, para incremetar mais tarde
              $textArray=array();     //Guardar as strings em cada linha
              $tmpString="";          //Guardar a string numa linha temporária

              while($startChar < $textLength){ //loop até ao fim do texto
                    //loop até chegar ao máximo de caracteres
                    while( 
                    $pdf->GetStringWidth( $tmpString ) < ($cellWidth-$errMargin) &&
                    ($startChar+$maxChar) < $textLength ) {
                          $maxChar++;
                          $tmpString=substr($r['produto'],$startChar,$maxChar);
                    }
                    //mover startChar para a próxima linha
                    $startChar=$startChar+$maxChar;
                    //depois adicionar para o array para saber quantas linhas serão necessárias
                    array_push($textArray,$tmpString);
                    //reset maxChar e tmpString
                    $maxChar=0;
                    $tmpString='';

              }
              //receber o numero de linhas
              $line=count($textArray);
        }

        //usar MultiCell em vez de Cell
        //mas primeiro, como a MultiCell é sempre tratada como fim de linha, precisamos de 
        //definir manualmente a posição xy para a próxima cell ficar ao lado.
        //guardar a posição x e y antes de escrever a multicell
        $xPos=$pdf->GetX();
        $yPos=$pdf->GetY();
        $pdf->MultiCell($cellWidth,$cellHeight,$r['produto'],1,'L');
        //receber a posição para a próxima cell ao lado da multicell
        //e equilibrar o x com o tamanho da multicell
        $pdf->SetXY($xPos + $cellWidth , $yPos);
        //escrever as cells
        $pdf->Cell(15,($line * $cellHeight),$r['quantidade'],1,0); //adaptar a altura ao número de linhas
        $pdf->Cell(10,($line * $cellHeight),'UNI',1,0); //adaptar a altura ao número de linhas

        $pdf->Cell(25,($line * $cellHeight),$r['precouni'].chr(128),1,0); //adaptar a altura ao número de linhas
        $pdf->Cell(25,($line * $cellHeight),$r['soma'].chr(128),1,1); //adaptar a altura ao número de linhas
  }

这是完整的 PHP 代码:

  class PDF extends TFPDF {
  // Page Header

      function Header() {
        require("../config.php");
        $rows = mysqli_query($link, "SELECT * FROM rostosativos_invoice INNER JOIN rostosativos_empresas ON rostosativos_invoice.empresa = rostosativos_empresas.empresa where id_proposta ='".$_GET['id']."';");
        $r = mysqli_fetch_assoc($rows);
        $id_empresa = $r['id_empresa'];
        $idproposta = $r['id_proposta'];
        $responsavel = $r['responsavel'];
        $empresa = $r['empresa'];
        $data = $r['data_registo'];
        $contribuinte = $r['contribuinte'];
        $assunto = $r['assunto'];
        $refcliente = $r['refcliente'];
        // Logo
        $this->SetY(4);
        $this->Image('../logo.png',10,6,30);
        // Arial bold 15
        $this->SetFont('Arial','B',9);
        // Move to the right
        $this->Cell(90);
        // Title
        $this->Cell(100,10,iconv('UTF-8', 'windows-1252','Sede: Rua Azenha dos Latoeiros, 1-A || 2580-557 Ribafria  '),'LTR',0,'C');
        $this->Ln(5);
        $this->Cell(90);
        $this->Cell(100,10,iconv('UTF-8', 'windows-1252','Oficina: Estrada Nacional nº1 km 33.3'),'LR',0,'C');
        $this->Ln(5);
        $this->Cell(90);
        $this->Cell(100,10,iconv('UTF-8', 'windows-1252','Quinta do Chacão, Casal Machado 2580-364 Alenquer '),'LR',0,'C');
        $this->Ln(5);
        $this->Cell(90);
        $this->Cell(100,10,iconv('UTF-8', 'windows-1252','E-mail: geral@rostosativos.com'),'LR',0,'C');
        $this->Ln(5);
        $this->Cell(90);
        $this->Cell(100,10,iconv('UTF-8', 'windows-1252','www.rostosativos.com'),'LR',0,'C');
        $this->Ln(5);
        $this->Cell(90);
        $this->Cell(100,10,iconv('UTF-8', 'windows-1252','www.facebook.com/rostosativos/'),'LBR',0,'C');
        // Line break

        $this->SetFont('Arial','',12);

        $this->Ln(15);

        $this->Cell(100 ,5,'',0,0);
        $this->Cell(35 ,5,'Proposta: ',0,0);
        $this->Cell(34 ,5, $idproposta,0,1);//end of line

        $this->Cell(100 ,5,'',0,0);
        $this->Cell(35 ,5,iconv('UTF-8', 'windows-1252','Ref. Cliente: '),0,0);
        $this->Cell(34 ,5,$refcliente,0,1);//end of line

        $this->Cell(100 ,5,'',0,0);
        $this->Cell(35 ,5,iconv('UTF-8', 'windows-1252','N.º Contribuinte: '),0,0);
        $this->Cell(34 ,5,$contribuinte,0,1);//end of line

        $this->Cell(100 ,5,'',0,0);
        $this->Cell(35 ,5,'Data: ',0,0);
        $this->Cell(34 ,5,$data,0,1);//end of line

        $this->Ln(5);

        //billing address
        $this->Cell(100 ,5,'Proposta para:',0,0);//end of line
        $this->Cell(100 ,5,'Assunto da proposta:',0,1);//end of line

        //add dummy cell at beginning of each line for indentation
        $this->Cell(10 ,5,'',0,0);
        $this->Cell(90 ,5,iconv('UTF-8', 'windows-1252',$empresa),0,0);

        $this->Cell(10 ,5,'',0,0);
        $this->Cell(90 ,5,iconv('UTF-8', 'windows-1252',$assunto),0,1);

        $this->Cell(10 ,5,'',0,0);
        $this->Cell(90 ,5,iconv('UTF-8', 'windows-1252',$responsavel),0,1);

        $this->Ln(2);
        //invoice contents
        $this->SetFont('Arial','B',12);

        $this->Cell(120 ,6.5,iconv('UTF-8', 'windows-1252','Designação'),1,0);
        $this->Cell(15 ,6.5,'Qtd.',1,0);
        $this->Cell(10 ,6.5,'UNI',1,0);
        $this->Cell(25, 6.5,iconv('UTF-8', 'windows-1252','Preço UNI.'),1,0);
        $this->Cell(25 ,6.5,'Total',1,1);//end of line

      }

      function Footer() {
        require("../config.php");
        //~Tabela de Preço, etc..
        $this->SetY(-20);
        $sql=sprintf("SELECT * FROM rostosativos_invoice where id_proposta = '".$_GET['id']."' ORDER BY id DESC LIMIT 1;");
        $res=mysqli_query($link, $sql);
        while ($r=mysqli_fetch_assoc($res)){

              $this->SetFont('Arial','',9);
              $this->Cell(30, 6,iconv('UTF-8', 'windows-1252','Exclusões:'),0,0);
              $this->Cell(15);
              $this->Cell(65 ,6,'',0,0);
              $this->SetFont('Arial','',11);
              $this->Cell(40 ,6,iconv('UTF-8', 'windows-1252','Soma'),0,0);
              $this->Cell(15);
              $this->Cell(30, 6, $r['totalsoma'].chr(128),1,1,'R');

              $this->SetFont('Arial','',9);
              $this->Cell(30,6,iconv('UTF-8', 'windows-1252',$r['exclusao1']),0, 'L');
              $this->SetFont('Arial','',11);
              $this->Cell(15);
              $this->Cell(65 ,6,'',0,0);
              $this->Cell(40 ,6,iconv('UTF-8', 'windows-1252','Mão de Obra'),0,0);
              $this->Cell(15);
              $this->Cell(30, 6, $r['maoobra'].chr(128),1,1,'R');

              $this->SetFont('Arial','',9);
              $this->Cell(30,6,iconv('UTF-8', 'windows-1252',$r['exclusao2']),0, 'L');
              $this->SetFont('Arial','',11);
              $this->Cell(80 ,6,'',0,0);
              $this->Cell(40 ,6,'Valor GLOBAL em EUROS',0,0);
              $this->Cell(15);
              $this->Cell(30 ,6,$r['precototal'].chr(128),1,1,'R');//end of line  
        }
        // Position at 1.5 cm from bottom
        $this->SetY(-9);
        // Arial italic 8
        $this->SetFont('Arial','I',8);
        // Page number
        $this->Cell(0,10,iconv('UTF-8', 'windows-1252','Página '.$this->PageNo().'/{nb}'),0,0,'C');
      }
  }
  //A4 width : 219mm
  //default margin : 10mm each side
  //writable horizontal : 219-(10*2)=189mm

  //create pdf object

  $pdf = new PDF('P','mm','A4');
  $pdf -> AliasNbPages();
  //add new page
  $pdf->AddPage();
  // Add a Unicode font (uses UTF-8)
  $pdf->AddFont('DejaVu','','DejaVuSansCondensed.ttf',true);
  $pdf->SetFont('DejaVu','',12);
  //set font to arial, regular, 12pt
  $pdf->SetFont('Arial','',12);

  $sql=sprintf("SELECT * FROM rostosativos_invoice where id_proposta = '".$_GET['id']."';");
  $res=mysqli_query($link, $sql);
  while ($r=mysqli_fetch_assoc($res)){

        // produto = posição 5
        // quantidade = posição 7
        // precouni = posição 6
        // soma = posição 9

        //multicell 
        $cellWidth=120;//tamanho da cell
        $cellHeight=6.5;//altura da cell

        //verificar se o texto passa a cell
        if($pdf->GetStringWidth($r['produto']) < $cellWidth){
              //se não, não fazer nada
              $line=1;
        }else{
              //~se estiver, ~então calcular a altura necessária para a cobrir a cell
              //ao dividir o texto para ajustar ao tamanho da cell
              //~depois contar quantas linhas são necessãrias para ajustar o texto na cell

              $textLength=strlen($r['produto']); //total text length
              $errMargin=10;          //cell com margem de erro, just in case
              $startChar=0;           //posição inicial para cada linha
              $maxChar=0;             //Máxima caracteres numa linha, para incremetar mais tarde
              $textArray=array();     //Guardar as strings em cada linha
              $tmpString="";          //Guardar a string numa linha temporária

              while($startChar < $textLength){ //loop até ao fim do texto
                    //loop até chegar ao máximo de caracteres
                    while( 
                    $pdf->GetStringWidth( $tmpString ) < ($cellWidth-$errMargin) &&
                    ($startChar+$maxChar) < $textLength ) {
                          $maxChar++;
                          $tmpString=substr($r['produto'],$startChar,$maxChar);
                    }
                    //mover startChar para a próxima linha
                    $startChar=$startChar+$maxChar;
                    //depois adicionar para o array para saber quantas linhas serão necessárias
                    array_push($textArray,$tmpString);
                    //reset maxChar e tmpString
                    $maxChar=0;
                    $tmpString='';

              }
              //receber o numero de linhas
              $line=count($textArray);
        }

        //usar MultiCell em vez de Cell
        //mas primeiro, como a MultiCell é sempre tratada como fim de linha, precisamos de 
        //definir manualmente a posição xy para a próxima cell ficar ao lado.
        //guardar a posição x e y antes de escrever a multicell
        $xPos=$pdf->GetX();
        $yPos=$pdf->GetY();
        $pdf->MultiCell($cellWidth,$cellHeight,$r['produto'],1,'L');
        //receber a posição para a próxima cell ao lado da multicell
        //e equilibrar o x com o tamanho da multicell
        $pdf->SetXY($xPos + $cellWidth , $yPos);
        //escrever as cells
        $pdf->Cell(15,($line * $cellHeight),$r['quantidade'],1,0); //adaptar a altura ao número de linhas
        $pdf->Cell(10,($line * $cellHeight),'UNI',1,0); //adaptar a altura ao número de linhas

        $pdf->Cell(25,($line * $cellHeight),$r['precouni'].chr(128),1,0); //adaptar a altura ao número de linhas
        $pdf->Cell(25,($line * $cellHeight),$r['soma'].chr(128),1,1); //adaptar a altura ao número de linhas
  }




  //output the result
  $pdf->Output();

  $content = $pdf->Output('propostas/'.$_GET['id'].'.pdf','F');
  file_put_contents($content);

在定义 PDF 和页面后立即进行这些更改。

$pdf = new PDF('P','mm','A4');
$pdf -> AliasNbPages();
$pdf->AddPage();
$pdf->SetAutoPageBreak(false);  // add this line and the next
$howhigh = $pdf->GetPageHeight();  // stash the height of the page for later

下一个更改是在 else 中计算较大单元格的大小。确实没有理由逐个字符地检查要添加的数据。您可以将该代码块替换为:

//verificar se o texto passa a cell
if ($pdf->GetStringWidth($r['produto']) < $cellWidth) {
    //se não, não fazer nada
    $line=1;
} else {
        $line = ceil($pdf->GetStringWidth($item[2]));
        $line = round($line / $cellWidth,0) + 1;
}

最后,数据的实际输出需要稍作改动,以便测试我们是否需要开始一个新页面。如您所见,使用了上面完成的计算。

//usar MultiCell em vez de Cell
//mas primeiro, como a MultiCell é sempre tratada como fim de linha, precisamos de 
//definir manualmente a posição xy para a próxima cell ficar ao lado.
//guardar a posição x e y antes de escrever a multicell
$xPos=$pdf->GetX();
$yPos=$pdf->GetY();
$total = $yPos + (($line * $cellHeight));
if ($total > $howhigh) {  // we will spill to a new page with this cell
    $pdf->AddPage();      // so start a new page before we add the cell
    $xPos=$pdf->GetX();
    $yPos=$pdf->GetY();
}
$pdf->MultiCell($cellWidth,$cellHeight,$r['produto'],1,'L');
//receber a posição para a próxima cell ao lado da multicell
//e equilibrar o x com o tamanho da multicell
$pdf->SetXY($xPos + $cellWidth , $yPos);