user156073
user156073

Reputation: 2001

line break problem with MultiCell in FPDF

I am using the Java port of FPDF. I am encountering the following errors.

  1. When I call multicell two times, every time the text is printed on a new line.
MultiCell(0, 1, "abcd", currentBorders, Alignment.LEFT, false); //prints on one line
MultiCell(0, 1, "efg", currentBorders, Alignment.LEFT, false); //prints on next line

I want there to be no line break after the call to multicell. How can I do it?

  1. If I do the following, then some part of my string gets printed on one line and some on the next.
MultiCell(getStringWidth(myString), 1, myStringcurrentBorders, Alignment.LEFT, false);
  1. If I do the following, then there are many blank lines after the line on which myString is printed. It works correctly if I use one 1 as the second parameter.
MultiCell(0, myFontSize, "123456", currentBorders, Alignment.LEFT, false);

What is the problem?

Upvotes: 20

Views: 69569

Answers (6)

Djordje Arsenovic
Djordje Arsenovic

Reputation: 23

I was struggling with multi-cell too, but it was a better solution to scale the text and make it fit.

So I've extended the class and called the CellFitScale function.

<?php
namespace App\Pdf;
use Fpdf\Fpdf;

//require('fpdf.php');
class FPDF_CellFit extends Fpdf
{
  //Cell with horizontal scaling if text is too wide
  function CellFit($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $scale=false, $force=true)
  {
    //Get string width
    $str_width=$this->GetStringWidth($txt);

    //Calculate ratio to fit cell
    if($w==0)
      $w = $this->w-$this->rMargin-$this->x;
    $ratio = ($w-$this->cMargin*2)/$str_width;

    $fit = ($ratio < 1 || ($ratio > 1 && $force));
    if ($fit)
    {
      if ($scale)
      {
        //Calculate horizontal scaling
        $horiz_scale=$ratio*100.0;
        //Set horizontal scaling
        $this->_out(sprintf('BT %.2F Tz ET',$horiz_scale));
      }
      else
      {
        //Calculate character spacing in points
        $char_space=($w-$this->cMargin*2-$str_width)/max(strlen($txt)-1,1)*$this->k;
        //Set character spacing
        $this->_out(sprintf('BT %.2F Tc ET',$char_space));
      }
      //Override user alignment (since text will fill up cell)
      $align='';
    }

    //Pass on to Cell method
    $this->Cell($w,$h,$txt,$border,$ln,$align,$fill,$link);

    //Reset character spacing/horizontal scaling
    if ($fit)
      $this->_out('BT '.($scale ? '100 Tz' : '0 Tc').' ET');
  }

  //Cell with horizontal scaling only if necessary
  function CellFitScale($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='')
  {
    $this->CellFit($w,$h,$txt,$border,$ln,$align,$fill,$link,true,false);
  }

  //Cell with horizontal scaling always
  function CellFitScaleForce($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='')
  {
    $this->CellFit($w,$h,$txt,$border,$ln,$align,$fill,$link,true,true);
  }

  //Cell with character spacing only if necessary
  function CellFitSpace($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='')
  {
    $this->CellFit($w,$h,$txt,$border,$ln,$align,$fill,$link,false,false);
  }

  //Cell with character spacing always
  function CellFitSpaceForce($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='')
  {
    //Same as calling CellFit directly
    $this->CellFit($w,$h,$txt,$border,$ln,$align,$fill,$link,false,true);
  }
}

And then in my PDF generation, I've used it like this:

$fpdf->CellFitScale(45, 8, trim('Some very long text!'), 1, 0, 'L');

I hope it helps. Cheers!

Upvotes: 0

سامي أوكتش
سامي أوكتش

Reputation: 31

@Muhammad Abdul Rahim and @tomazahlin have have provided good methods. The problem they only sort out the line break problem in a single cell. They don't match the height of the subject cell with other cells in the same row. Using GetY() gets complicated if you are dealing with dynamic tables. The simplest solution I have found is identifying the column likely to have overflowing text and using it as a reference.

$l=strlen($string_of_reference_cell);
$h = ceil($l/$cell_width*1.5)*preferred_normal_height;//1.5 is a loading for allowance`depending on font size.

$pdf->cell(20,$h,$string,1,0);
$pdf->MultiAlignCell(50,5,$string_of_reference_cell,1,0);// 5 is the preferred normal height
$pdf->Cell(23,$h,$string,1,1);

When the pdf is generated, if the string of MultiAlignCell is longer than the cell width, a line break is generated. The resulting height is twice(5 x 2 = 10). The height of ten is asigned to $h. Hence the other cells take the height of $h as well and the entire row gets a uniform height.

Upvotes: 0

Gama
Gama

Reputation: 61

in my case I didn't create any method, I just set X and Y and then at the end of the line I reset. It works perfectly too.

        $pdf->SetFont('times', 'B', 10);
        $x = $pdf->GetX();
        $y = $pdf->GetY();
        $pdf->MultiCell($etiquetas_largura, $etiquetas_altura, $campos[$linha]['B1COD'], 0, 'L', 0, 0, $x, $y, true, 0, false, true, 0);
        $y = $y + 5;
        $pdf->SetFont('times', '', 10);
        $pdf->MultiCell($etiquetas_largura, $etiquetas_altura, $campos[$linha]['B1DESC'], 0, 'L', 0, 0, $x, $y, true, 0, false, true, 0);
        // resete x y
        $pdf->SetXY($x + $etiquetas_largura, $y - 5);

Upvotes: 0

Joshua Pinter
Joshua Pinter

Reputation: 47471

I would get the current Y position before writing the MultiCell and then move the "cursor" back to that Y position after the MultiCell generation. Like this:

$current_y = $pdf->GetY();
$current_x = $pdf->GetX();

$cell_width = 50;
MultiCell($cell_width, 1, "abcd", currentBorders, Alignment.LEFT, false);

$pdf->SetXY($current_x + $cell_width, $current_y);

$current_x = $pdf->GetX();
MultiCell($cell_width, 1, "abcd", currentBorders, Alignment.LEFT, false);

Something like that.

Upvotes: 25

Muhammad Abdul-Rahim
Muhammad Abdul-Rahim

Reputation: 2010

I created a new method called MultiAlignCell. It takes the same parameters as MultiCell but with the added ln field from Cell. You can add it to your extended FPDF class.

/**
* MultiCell with alignment as in Cell.
* @param float $w
* @param float $h
* @param string $text
* @param mixed $border
* @param int $ln
* @param string $align
* @param boolean $fill
*/
private function MultiAlignCell($w,$h,$text,$border=0,$ln=0,$align='L',$fill=false)
{
    // Store reset values for (x,y) positions
    $x = $this->GetX() + $w;
    $y = $this->GetY();

    // Make a call to FPDF's MultiCell
    $this->MultiCell($w,$h,$text,$border,$align,$fill);

    // Reset the line position to the right, like in Cell
    if( $ln==0 )
    {
        $this->SetXY($x,$y);
    }
}

Upvotes: 6

tomazahlin
tomazahlin

Reputation: 2167

I have modified the MultiCell method, it works as the above answer, and you can use the method in the same way as the Cell method.

function MultiCell($w, $h, $txt, $border=0, $ln=0, $align='J', $fill=false)
{
    // Custom Tomaz Ahlin
    if($ln == 0) {
        $current_y = $this->GetY();
        $current_x = $this->GetX();
    }

    // Output text with automatic or explicit line breaks
    $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(strpos($border,'L')!==false)
                $b2 .= 'L';
            if(strpos($border,'R')!==false)
                $b2 .= 'R';
            $b = (strpos($border,'T')!==false) ? $b2.'T' : $b2;
        }
    }
    $sep = -1;
    $i = 0;
    $j = 0;
    $l = 0;
    $ns = 0;
    $nl = 1;
    while($i<$nb)
    {
        // Get next character
        $c = $s[$i];
        if($c=="\n")
        {
            // Explicit line break
            if($this->ws>0)
            {
                $this->ws = 0;
                $this->_out('0 Tw');
            }
            $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
            $i++;
            $sep = -1;
            $j = $i;
            $l = 0;
            $ns = 0;
            $nl++;
            if($border && $nl==2)
                $b = $b2;
            continue;
        }
        if($c==' ')
        {
            $sep = $i;
            $ls = $l;
            $ns++;
        }
        $l += $cw[$c];
        if($l>$wmax)
        {
            // Automatic line break
            if($sep==-1)
            {
                if($i==$j)
                    $i++;
                if($this->ws>0)
                {
                    $this->ws = 0;
                    $this->_out('0 Tw');
                }
                $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
            }
            else
            {
                if($align=='J')
                {
                    $this->ws = ($ns>1) ?     ($wmax-$ls)/1000*$this->FontSize/($ns-1) : 0;
                    $this->_out(sprintf('%.3F Tw',$this->ws*$this->k));
                }
                $this->Cell($w,$h,substr($s,$j,$sep-$j),$b,2,$align,$fill);
                $i = $sep+1;
            }
            $sep = -1;
            $j = $i;
            $l = 0;
            $ns = 0;
            $nl++;
            if($border && $nl==2)
                $b = $b2;
        }
        else
            $i++;
    }
    // Last chunk
    if($this->ws>0)
    {
        $this->ws = 0;
        $this->_out('0 Tw');
    }
    if($border && strpos($border,'B')!==false)
        $b .= 'B';
    $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
    $this->x = $this->lMargin;

    // Custom Tomaz Ahlin
    if($ln == 0) {
        $this->SetXY($current_x + $w, $current_y);
    }
}

Upvotes: 3

Related Questions