如何计算 TCPDF 中 MultiCell/writeHTMLCell 的高度?

问题描述 投票:0回答:8

我尝试创建一个包含多个页面的 PDF,需要提前计算每个单独元素 (MultiCell) 的高度,为分页做好准备。根据文档,有几个函数(例如 GetCharWidth/GetStringWidth)可以支持我自己完成此操作,但除了潜在的性能损失之外,我可能无论如何也不会正确执行此操作。以更优雅的方式实现我的目标的建议?

参考:

TCPDF

php pdf tcpdf
8个回答
30
投票
我明白了:D!!!!!!

创建另一个 pdf2 对象

// pdf2 set x margin to pdf1's xmargin, but y margin to zero // to make sure that pdf2 has identical settings, you can clone the object (after initializing the main pdf object) $pdf2 = clone $pdf; pdf2->addpage pdf2->writeCell $height = pdf2->getY() pdf2->deletePage(pdf2->getPage()) pdf1->checkPageBreak($height); pdf1->writeCell()

W00tness :D


24
投票
这是一个老问题,但 TCPDF 的当前版本(截至 2011 年 12 月 7 日)有一个名为

getStringHeight

 的函数,它允许您在实际调用 MultiCell 之前计算传递给 MultiCell 的字符串的最终高度。然后这个高度可以用于各种事情,原始问题中的计算,也可以用于在制作表格等时设置行高。效果很好。

只是一些信息,以防其他人像我一样偶然发现这个问题并寻找解决方案。


10
投票
虽然 Carvell 的答案很好,但 TCPDF 提到

getStringHeight

 返回估计高度。有用的是,那里的文档提供了一种非常全面的技术来获取准确的高度,结果为 
$height
。至于为什么他们自己不使用这个是一个谜......

// store current object $pdf->startTransaction(); // store starting values $start_y = $pdf->GetY(); $start_page = $pdf->getPage(); // call your printing functions with your parameters // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $pdf->MultiCell($w=0, $h=0, $txt, $border=1, $align='L', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // get the new Y $end_y = $pdf->GetY(); $end_page = $pdf->getPage(); // calculate height $height = 0; if ($end_page == $start_page) { $height = $end_y - $start_y; } else { for ($page=$start_page; $page <= $end_page; ++$page) { $pdf->setPage($page); if ($page == $start_page) { // first page $height = $pdf->h - $start_y - $pdf->bMargin; } elseif ($page == $end_page) { // last page $height = $end_y - $pdf->tMargin; } else { $height = $pdf->h - $pdf->tMargin - $pdf->bMargin; } } } // restore previous object $pdf = $pdf->rollbackTransaction();
    

5
投票
根据我的经验,提前计算出单元高度几乎是不可能的。使用 TCPDF 的分页符处理功能要容易得多,它会提前告诉您是否要进入分页符。这是示例代码:

$yy = $this->pdf->GetY(); $check_pagebreak = $this->pdf->checkPageBreak($height+$padding,$yy,false);

将 false 更改为 true 以允许自动分页,否则,您可以自己处理分页的逻辑,这就是我最终所做的。

此外,如果您可能需要这个,这里还有另一个小提示:考虑使用事务功能分两次创建文档。第一遍用于计算所有高度和单元格、分页符等。您还可以将所有行高和每页行数存储在数组中。在第二遍中,使用所有正确的信息创建文档,不需要分页逻辑(第二遍可以从单独的方法运行,以便使代码更易于阅读,并保持理智)。


3
投票
使用 TCPDF 示例 20

  1. 如果单元格/列在不同页面上结束,计算多单元格高度可能是一场噩梦。

  2. 使用事务或附加 pdf 对象会使速度变得非常慢。

  3. 在打印单元格之前使用 getNumLines() 和 getStringHeight() 等函数来计算“估计”(请参阅文档)高度并不总是能正常工作。特别是如果文本恰好在单元格右边框之前或之后结束 - 导致行打印在彼此之上。

我更喜欢

示例 20 中使用的技术,其中使用不同页面的最大 Y 值来计算新行的位置。

该示例仅打印两列,但我更改了其主要功能以能够打印列数组。显然你可以向数组添加更多数据,例如每列的字体、边框等。

public function MultiRow($columnsArray) { $page_start = $this->getPage(); $y_start = $this->GetY(); $pageArray = array(); $yArray = array(); // traverse through array and print one column at a time. $columnCount = count($columnsArray); for($i=0; $i<$columnCount; $i++) { if($i+1 < $columnCount) { // Current column is not the last column in the row. // After printing, the pointer will be moved down to // the right-bottom of the column - from where the // next multiCell in the following loop will use it // via $this->GetX(). $ln = 2; } else { // Current column is the last column in the row. // After printing, the pointer will be moved to new line. $ln = 1; } $this->MultiCell(30, 0, $columnsArray[$i], 1, 'L', 1, $ln, $this->GetX() ,$y_start, true, 0); $pageArray[$i] = $this->getPage(); $yArray[$i] = $this->GetY(); // Go to page where the row started - to print the // next column (if any). $this->setPage($page_start); } // Test if all columns ended on the same page $samePage = true; foreach ($pageArray as $val) { if($val != $pageArray['0']) { $samePage = false; break; } } // Set the new page and row position by case if($samePage == true) { // All columns ended on the same page. // Get the longest column. $newY = max($yArray); } else { // Some columns ended on different pages. // Get the array-keys (not the values) of all columns that // ended on the last page. $endPageKeys = array_keys($pageArray, max($pageArray)); // Get the Y values of all columns that ended on the last page, // i.e. get the Y values of all columns with keys in $endPageKeys. $yValues = array(); foreach($endPageKeys as $key) { $yValues[] = $yArray[$key]; } // Get the largest Y value of all columns that ended on // the last page. $newY = max($yValues); } // Go to the last page and start at its largets Y value $this->setPage(max($pageArray)); $this->SetXY($this->GetX(),$newY); }
    

1
投票
帖子

重访:Tcpdf – 具有多单元格的可变高度表行有很多有用的信息。这是一个简短的摘录:

getNumLines()

 ...实际上允许我们确定给定特定宽度的文本字符串将占用多少行。实际上,它允许我们执行我使用 MultiCell 返回的操作,而无需实际绘制任何内容。这让我们可以用一行代码确定最大单元高度:

$linecount = max($pdf->getNumLines($row['cell1data'], 80),$pdf->getNumLines($row['cell2data'], 80

    

0
投票
我尝试使用杰伊的答案,它达到了预期目的,但由于某种原因导致我的徽标没有出现在第一页之后。我不想做深入的分析,但与克隆有关。然后我尝试了相同的方法,但使用事务。这产生了数百个错误。

然后我使用同一个对象想出了这个相当简单的解决方案。

/** * Gets an accurate measurement of a cell's rendered height. * * @param float $width the width of the column to be rendered * @param string $contents the contents to be rendered * * @return float */ private function getCellHeight(float $width, string $contents): float { $view = $this->view; $currentPage = $view->getPage(); $currentX = $view->GetX(); $currentY = $view->GetY(); $view->AddPage(); $x = $view->GetX(); $start = $view->GetY(); $view->writeHTMLCell($width, 15, $x, $start, $contents, self::INSTANCE_BORDER, 1); $height = $view->GetY() - $start; $view->deletePage($view->getPage()); $view->setPage($currentPage); $view->changePosition($currentX, $currentY); return $height; }
由于 

writeHTMLCell

 函数需要 
$h
,因此我使用 
15
,但它可以是您想要的任何内容,
$border
 值也可以。

$ln
值需要设置为
1
,否则
y
值会在
GetY()
获取之前重置。

changePosition
 是我自己的 
SetXY()
 包装。


0
投票
随着 Rob Forrest 的回答,我必须在

$pdf->Ln()

 之后添加另一个 
$pdf->MulticCell()
 才能获得 
$pdf->GetY()
 正确的值

© www.soinside.com 2019 - 2024. All rights reserved.