Search code examples
phpmysqlpdffpdf

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


I am using PHP and fPDF to create a PDF "invoice" and everything works perfectly fine on the first page but when the table has to go to the second page it only returns the first value from the sql query and everything goes to the rest goes to the third page and so on.

pdf image

This is the code that loops the table rows

  $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
  }

This is the full PHP code:

  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);

Solution

  • Make these changes right after you define the PDF and pages.

    $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
    

    The next change is in the else where the size of the larger cells is calculated. There is really no reason to go through the data to be added character by character. You can replace that block of code with:

    //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;
    }
    

    Finally, the actual output of the data needs a little change to account for testing whether or not we need to start a new page. As you'll see the calculation done above are used.

    //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);