Reputation: 1172
I use the FPDF library to export some document files as PDF. One document includes a list of strings which have a different length. I print all strings as $pdf->MultiCell()
. Now I would like to have the current height of that MultiCell to have the same line spacing in case that they have just one line or more.
Code Example:
//MySQL Query
while($row = mysql_fetch_array($res) {
$pdf->SetXY(18, $x);
$pdf->MultiCell(80, 5, $rowr['text']); //text has one or more lines
$x = $x + 10; // Here I would prefer a solution to say: $x = $x + 2 + height of the MultiCell()
}
Upvotes: 11
Views: 44109
Reputation: 360
Wouldn't it be easier to simply NOT use boarders when you print out the text.
You can just use $pdf->GetY(); to get the curent y value.
Then when you have printed all the text, you can use $pdf->GetY(); to get the height after each piece of text. Compare y values to see which one is the biggest.
All you need to do then is $pdf-> SetY($y); to the original y value and paint the boarders with $pdf->Cell() now that you know height and width;
That's how I'd do it.
Edit: tested it, and seems to work. Now sing with me - "you get the beeeeest of both worlds..." no?
$cellData = array();
$cellData[0] = array();
$cellData[0][0] = array();
$cellData[0][0]['text'] = 'Audiometry';
$cellData[0][0]['width'] = '47';
$cellData[0][1] = array();
$cellData[0][1]['text'] = 'Control of Noise at Work Regulations 2005.';
$cellData[0][1]['width'] = '47';
$cellData[0][2] = array();
$cellData[0][2]['text'] = 'Fit with restrictions (as detailed below) to work in a noise controlled zone.';
$cellData[0][2]['width'] = '47';
$cellData[0][3] = array();
$cellData[0][3]['text'] = 'Recommended review date: 2021-11-27
Referral: Referred to OHP';
$cellData[0][3]['width'] = '47';
setAllCellSizes($cellData, $pdf);
function setAllCellSizes($cellData, $pdf){
$y = $pdf->GetY();
$x = $pdf->GetX();
$largestCell = 0;
for($cordI = 0; $cordI < count($cellData); $cordI++){
$curX = 10;
for($cordJ = 0; $cordJ < count($cellData[$cordI]); $cordJ++){
$pdf->SetXY($curX,$y);
$pdf-> MultiCell($cellData[$cordI][$cordJ]['width'], 5, $cellData[$cordI][$cordJ]['text'], '0', 'L');
$curX = $curX + $cellData[$cordI][$cordJ]['width'];
$cellHeight = $pdf->GetY() - $y;
if($largestCell < $cellHeight){
$largestCell = $cellHeight;
}
}
$curX = 10;
for($cordJ = 0; $cordJ < count($cellData[$cordI]); $cordJ++){
$pdf->SetXY($curX,$y);
$pdf->Cell($cellData[$cordI][$cordJ]['width'],$largestCell,'',1,1,'L');
$curX = $curX + $cellData[$cordI][$cordJ]['width'];
}
}
return $cellData;
}
Upvotes: 0
Reputation: 3292
I'm coding in golang so I'll show some pseudo-code. I hope the accessible methods are the same in php
as in golang
.
There is a method called pdf.SplitLines(text, width)
. You will pass your string content and the desired width and it will return an array of strings that represents the lines that'll be computed to display that content.
With that its easy. In pseudo-code it could look like:
fontSize = 10;
lineHeight = 12;
targetWidth = 50;
pdf.SetFontSize(fontSize)
nLines = length(pdf.SplitLines(content, targetWidth));
multiCellHeight = nLines * lineHeight;
pdf.Multicell(targetWidth, lineHeight, content, ...)
The rendered MultiCell
will have the exact same size as stored in multiCellHeight
. This way you'll get the hight before rendering.
This is working because the passed height
for the MultiCell
is the lineHeight
of each row. If you know the rows before rendering, you'll get the total height.
I'm sorry if this fails for any reason for php
. Just let me know if that's the case.
Upvotes: 2
Reputation: 146
I found interesting solution here - https://github.com/artkonekt/pdf-invoice/blob/master/src/InvoicePrinter.php#L469
$calculateHeight = new self;
$calculateHeight->addPage();
$calculateHeight->setXY(0, 0);
$calculateHeight->SetFont($this->font, '', 7);
$calculateHeight->MultiCell($this->firstColumnWidth, 3, $item['description'], 0, 'L', 1);
$descriptionHeight = $calculateHeight->getY() + $cellHeight + 2;
So, he literally create a 'temporary' PDF, add multicell, and then simply measure height (newY - oldY)
Also, keep in mind that if text goes to new line - height of cell will be = number_of_lines * $height
(height passed to MultiCell as second parameter)
So, if you passed 5 as $height, and temporary PDF measure that cell will be 15, you can be sure that text will spread to 3 lines.
Upvotes: 4
Reputation:
I had the exact same problem; I use FPDF to generate invoices and there are four cells per row with first cell being a MultiCell with varying height (default height is 5, but if order name is too long it adds another line to a total height of 10, and so on). The problem is that the remaining 3 cells have fixed height.
After looking for solutions it seems the only way is to write a complex function or use some third party tool. Since I want my app to be as light as possible I used the following approach to solve it which in my opinion is way simpler than external plugin.
$Y=95;
before my while loopMy first cell (the MultiCell) is as follows:
$pdf->SetXY(10,$Y);
$pdf->MultiCell(60,5,$row['Name'],1,1,'L');
I use the FPDF's GetY() function to get current height and save it as H:
$H = $pdf->GetY();
If the MultiCell's height is 5 GetY() will give back 100, if the height is 10 GetY() gives back 105 and so on.
I add new variable $height:
$height= $H-$Y;
Which as result gives me precisely the height of the MultiCell.
I use $Y and $height to set current position and column height:
$pdf->SetXY(130,$Y);
$pdf->Cell(40,$height,$row['RowName'],1,1,'L');
Before finishing the while loop set give $Y the $H's value:
$Y=$H;
Entire loop looks as follows and works perfectly:
$Y= 95;
$query = mysqli_query($con,"SELECT * FROM table");
while($row = mysqli_fetch_array($query)) {
$pdf->SetXY(10,$Y);
$pdf->MultiCell(60,5,$row['ROW1'],1,1,'L');
$H = $pdf->GetY();
$height= $H-$Y;
$pdf->SetXY(70,$Y);
$pdf->Cell(60,$height,$row['ROW2'],1,1,'L');
$pdf->SetXY(130,$Y);
$pdf->Cell(40,$height,$row['ROW3'],1,1,'L');
$pdf->SetXY(170,$Y);
$pdf->Cell(30,$height,$row['ROW4'],1,1,'L');
$Y=$H;
}
If you have 2 MultiCell columns or more in each row it gets tricky but still can be solved in a similar manner.
Upvotes: 25