How to calculate the height of a MultiCell/writeHTMLCell in TCPDF?

爷,独闯天下 提交于 2019-12-18 12:58:18

问题


I try to create a PDF with multiple pages and need to calculate the height of each individual element (MultiCell) in advance to prepare for a page break. According to the documentation there are a couple of functions out there like GetCharWidth/GetStringWidth to support me in doing it on my own, but besides a potential performance lost I probably will not do it the right anyway. Suggestions to achieve my goal in a more elegant way?

Reference: TCPDF


回答1:


I GOT it :D!!!!!

Create another pdf2 object

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




回答2:


This is an old question, but the current version (as of 7 Dec 2011) of TCPDF has a function called getStringHeight that allows you to calculate the resulting height of a string passed to MultiCell prior to actually calling MultiCell. Then this height can be used for various things, the calculation in the original question, and also for setting row height when making tables etc. Works great.

Just some info in case someone else stumbles across this question looking for a solution to this problem as I did.




回答3:


While Carvell's answer is great, TCPDF mentions that getStringHeight returns the estimated height. Helpfully the documentation there provides a pretty comprehensive technique for getting the exact height which comes out as $height. As to why they don't use this themselves is a mystery...

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



回答4:


From my experience, it is nearly impossible to figure out the cell height in advance. It is much easier to use the page break handling functions of TCPDF that sort of tells you in advance if you're heading into a pagebreak. Here is example code:

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

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

Change false to true to allow for an automatic page break, otherwise, you can handle the logic of the pagebreak, yourself, which is what I ended up doing.

Also, in case you may need this, here's another little tip: Consider using the transaction features to create your document in two passes. The first pass is used to figure out all the heights and cells, pagebreaks, etc. You can also store all your lineheights and lines per page in arrays. ON the second pass, create your document with all the correct information and no need for pagebreak logic (the second pass could be run from a seperate method, for the sake of keeping the code easier to read, and for your sanity).




回答5:


Use TCPDF Example 20

  1. Calculating MultiCell heights can be a nightmare if the cells/columns end on different pages.

  2. Using transactions or additional pdf objects can make things very slow.

  3. Using functions such as getNumLines() and getStringHeight() to calculate the 'estimated' (see docs) height before the cells are printed do not always work correctly. Especially if the text ends just before or just after the right border of the cell - resulting in rows being printed on top of each other.

I prefer the technique used in Example 20 where the maximum Y value of the different pages are used to calculate the position of the new row.

The example prints only two columns, but I changed its main function to be able to print an array of columns. Obviously you could add more data to the array, such as each column's font, borders, etc.

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



回答6:


The post Revisited: Tcpdf – Variable Height Table Rows With MultiCell has a lot of useful information. This is a short extract:

getNumLines() ... actually allows us to determine how many lines a string of text will take up, given a particular width. In effect, it allows us to do what I was using MultiCell to return, without actually drawing anything. This lets us to determined the maximum cell height with one line of code:

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


来源:https://stackoverflow.com/questions/1078729/how-to-calculate-the-height-of-a-multicell-writehtmlcell-in-tcpdf

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!