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

时间:2009-07-03 10:12:41

标签: php pdf tcpdf

我尝试创建一个包含多个页面的PDF,并且需要计算每个元素的高度(MultiCell)提前以准备分页符。根据文档,有一些函数,如GetCharWidth / GetStringWidth支持我自己做,但除了潜在的性能丢失,我可能不会做正确的权利。建议以更优雅的方式实现我的目标?

参考:TCPDF

7 个答案:

答案 0 :(得分:24)

我认为: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

答案 1 :(得分:15)

这是一个老问题,但TCPDF的当前版本(截至2011年12月7日)有一个名为getStringHeight的函数,它允许您在实际调用MultiCell之前计算传递给MultiCell的字符串的结果高度。然后,这个高度可用于各种事物,原始问题中的计算,以及制作表格时设置行高等。工作得很好。

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

答案 2 :(得分:7)

虽然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();

答案 3 :(得分:4)

根据我的经验,几乎不可能提前计算出细胞高度。使用TCPDF的分页处理功能要容易得多,如果您正在进入分页符,请提前告诉您。以下是示例代码:

$yy = $this->pdf->GetY();

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

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

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

答案 4 :(得分:2)

使用TCPDF示例20

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

  2. 使用事务或其他pdf对象会使事情变得非常缓慢。

  3. 使用getNumLines()和getStringHeight()等函数来计算估计的&#39; (请参阅docs)打印单元格之前的高度并不总是正常工作。特别是如果文本在单元格的右边界之前或之后结束 - 导致行相互打印。

  4. 我更喜欢Example 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);
    }
    

答案 5 :(得分:1)

帖子Revisited: Tcpdf – Variable Height Table Rows With MultiCell有很多有用的信息。这是一个简短的摘录:

  

getNumLines() ...实际上允许我们在给定特定宽度的情况下确定一串文本将占用多少行。实际上,它允许我们执行我使用MultiCell返回的内容,而不实际绘制任何内容。这让我们用一行代码确定最大单元格高度:

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

答案 6 :(得分:0)

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

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

/**
 * 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() 包装器。