2009-07-03 67 views
9

Intento crear un PDF con varias páginas y necesito calcular la altura de cada elemento individual (MultiCell) por adelantado para prepararme para un salto de página. De acuerdo con la documentación, hay un par de funciones como GetCharWidth/GetStringWidth para ayudarme a hacerlo por mi cuenta, pero además de perder un potencial rendimiento, probablemente no lo haga de todos modos. Sugerencias para lograr mi objetivo de una manera más elegante?¿Cómo se calcula la altura de un MultiCell/writeHTMLCell en TCPDF?

Referencia: TCPDF

Respuesta

1

El puesto Revisited: Tcpdf – Variable Height Table Rows With MultiCell tiene una gran cantidad de información útil. Este es un extracto corto:

getNumLines() ... en realidad nos permite determinar cuántas líneas tomará una cadena de texto, dado un ancho particular. En efecto, nos permite hacer lo que estaba usando MultiCell para devolver, sin realmente dibujar nada. Esto nos permite que determina la altura máxima de células con una línea de código:

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

Ambos getNumLines() y getStringHeight() sólo dar una 'altura estimada' (ver documentos). Estas funciones no siempre funcionan correctamente si el texto termina justo antes o justo después del borde derecho de la celda, lo que hace que las filas se impriman una sobre otra. Más bien use la técnica en el ejemplo 20. –

3

Desde mi experiencia, es casi imposible averiguar la altura de la celda de antemano. Es mucho más fácil usar las funciones de manejo de ruptura de página de TCPDF que, de alguna manera, le dice de antemano si se dirige a un salto de página. Aquí es código de ejemplo:

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

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

Cambio false a true para permitir un salto de página automático, de lo contrario, puede manejar la lógica del salto de página, usted mismo, que es lo que terminé haciendo.

Además, en caso de que lo necesite, aquí hay otro pequeño consejo: considere usar las funciones de transacción para crear su documento en dos pasos. El primer pase se usa para calcular todas las alturas y celdas, saltos de página, etc. También puede almacenar todas sus líneas y líneas por página en matrices. En el segundo pase, cree su documento con toda la información correcta y sin necesidad de lógica de salto de página (el segundo pase podría ejecutarse desde un método separado, por el simple hecho de mantener el código más fácil de leer y por su cordura).

21

I GOT it: D !!!!!

Crear otro objeto 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

+0

Al principio era un poco escéptico con respecto a esta respuesta, pero funciona bastante bien. En realidad mejor que cualquier otro método que he probado hasta ahora. Aunque no debes olvidar poner $ pdf2 en la misma fuente que $ pdf1 –

+0

En realidad funciona bastante bien para ser justo. Si está clonando, la fuente debería ser la misma de todos modos. Si está usando esto para generar dinámicamente tablas, no olvide que los anchos también son importantes. – LeonardChallis

+0

Esto no funciona para MultiCell si $ ln = 0, porque entonces el cursor se mueve hacia la derecha y getY() no da la parte inferior del celda "más alta" (si el texto era demasiado largo para caber en ella, por lo tanto, diferentes alturas de celda). –

13

Ésta es una cuestión de edad, pero la versión actual (al 7 de diciembre de 2011) de TCPDF tiene una función llamada getStringHeight que le permite calcule la altura resultante de una cadena pasada a MultiCell antes de llamar a MultiCell. Entonces, esta altura se puede usar para varias cosas, el cálculo en la pregunta original y también para establecer la altura de la fila al hacer tablas, etc. Funciona muy bien.

Solo algo de información en caso de que alguien más tropiece con esta pregunta buscando una solución a este problema como yo lo hice.

+0

"... También para configurar la altura de fila al hacer tablas" ==> SÍ. Estuve un poco desconcertado al descubrir que Multicells no establece automáticamente la altura de la fila a la altura de la celda más alta. Pero usar el máximo de las alturas del texto en la fila, obtenido con este método, es una solución bastante fácil. –

6

Mientras que la respuesta de Carvell es excelente, TCPDF menciona que getStringHeight devuelve la altura estimada. La documentación allí brinda una técnica muy completa para obtener la altura exacta que aparece como $height. En cuanto a por qué no usan esto ellos mismos es un misterio ...

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

Esto es un problema si $ ln = 0, porque entonces $ start_y = $ end_y. Desearía que almacenaran el valor de Y, antes de mover el cursor hacia la derecha. –

+0

Así que puede que tenga que usar $ ln = 1 y reiniciar yy X después de cada MultiCell. –

+2

¡NOTA! En la versión más reciente, algunas propiedades están protegidas y debe cambiar lo siguiente: '$ pdf-> h' a' $ pdf-> getPageHeight() ',' $ pdf-> bMargin' a '$ pdf-> getBreakMargin()' , y '$ pdf-> tMargin' a' $ pdf-> getMargins() ['top'] ' – TheStoryCoder

1

Uso TCPDF Ejemplo 20

  1. Cálculo de alturas MultiCell puede ser una pesadilla si las células/columnas terminan en diferentes páginas.

  2. El uso de transacciones u objetos pdf adicionales puede hacer las cosas muy lentas.

  3. El uso de funciones como getNumLines() y getStringHeight() para calcular la altura 'estimada' (ver documentos) antes de imprimir las celdas no siempre funciona correctamente. Especialmente si el texto termina justo antes o justo después del borde derecho de la celda, lo que hace que las filas se impriman una sobre otra.

I prefieren la técnica utilizada en Example 20 cuando el valor máximo Y de las distintas páginas se utilizan para calcular la posición de la nueva fila.

El ejemplo imprime solo dos columnas, pero cambié su función principal para poder imprimir una matriz de columnas. Obviamente, se podría añadir más datos a la matriz, como la fuente de cada columna, bordes, 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); 
} 
Cuestiones relacionadas