2009-07-03 15 views
9

Provo a creare un PDF con più pagine e devo calcolare l'altezza di ogni singolo elemento (MultiCell) in anticipo per preparare un'interruzione di pagina. Secondo la documentazione ci sono un paio di funzioni là fuori come GetCharWidth/GetStringWidth per supportarmi nel farlo da solo, ma oltre a una potenziale prestazione persa probabilmente non lo farò comunque. Suggerimenti per raggiungere il mio obiettivo in un modo più elegante?Come calcolare l'altezza di un MultiCell/writeHTMLCell in TCPDF?

Riferimento: TCPDF

risposta

1

Il post Revisited: Tcpdf – Variable Height Table Rows With MultiCell ha un sacco di informazioni utili. Questo è un breve estratto:

getNumLines() ... in realtà ci consente di determinare quante linee una stringa di testo occuperà, data una larghezza particolare. In effetti, ci permette di fare ciò che stavo usando MultiCell per tornare, senza effettivamente disegnare nulla. Questo ci permette di determinare l'altezza massima delle cellule con una sola riga di codice:

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

Entrambi getNumLines() e getStringHeight() solo dare un 'altezza stimata' (vedi documentazione). Queste funzioni non funzionano sempre correttamente se il testo termina poco prima o subito dopo il bordo destro della cella, con il risultato che le righe vengono stampate l'una sull'altra. Piuttosto usa la tecnica nell'esempio 20. –

3

Dalla mia esperienza, è quasi impossibile capire l'altezza della cella in anticipo. È molto più semplice utilizzare le funzioni di gestione delle interruzioni di pagina di TCPDF che ti dicono in anticipo se stai andando in un pagebreak. Ecco codice di esempio:

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

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

Change false a true per consentire un'interruzione di pagina automatica, in caso contrario, è possibile gestire la logica del pagebreak, da soli, che è quello che ho finito per fare.

Inoltre, nel caso in cui sia necessario, ecco un altro piccolo consiglio: considera l'utilizzo delle funzionalità di transazione per creare il documento in due passaggi. Il primo passaggio viene utilizzato per calcolare tutte le altezze e le celle, i pagebreaks, ecc. È anche possibile memorizzare tutti gli allineamenti e le linee per pagina negli array. Al secondo passaggio, crea il tuo documento con tutte le informazioni corrette e senza bisogno di logiche di interruzioni di pagina (il secondo passaggio potrebbe essere eseguito da un metodo separato, al fine di mantenere il codice più facile da leggere e per la tua sanità mentale).

21

HO FATTO: D !!!!!

Creare un altro oggetto 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

All'inizio ero un po 'scettico riguardo a questa risposta, ma funziona abbastanza bene. In realtà meglio di qualsiasi altro metodo che ho provato finora. Anche se non dovresti dimenticare di mettere $ pdf2 sullo stesso font di $ pdf1 –

+0

In realtà funziona abbastanza bene per essere giusto. Se stai clonando, il font dovrebbe essere lo stesso comunque. Se stai usando questo per generare dinamicamente tabelle, non dimenticare che anche le larghezze sono importanti – LeonardChallis

+0

Questo non funziona con MultiCell se $ ln = 0, perché allora il cursore si sposta a destra e getY() non fornisce il fondo del cella "più alta" (se il testo era troppo lungo per adattarsi a esso - quindi altezze delle celle diverse). –

13

Questa è una vecchia questione, ma la versione corrente (al 7 dicembre 2011) di TCPDF ha una funzione chiamata getStringHeight che permette di calcolare l'altezza risultante di una stringa passata a MultiCell prima di chiamare effettivamente MultiCell. Quindi questa altezza può essere utilizzata per varie cose, il calcolo nella domanda originale, e anche per impostare l'altezza della riga quando si creano tabelle, ecc. Funziona alla grande.

Solo alcune informazioni nel caso in cui qualcun altro si imbattesse in questa domanda cercando una soluzione a questo problema come ho fatto io.

+0

"... anche per impostare l'altezza della riga quando si eseguono le tabelle" ==> SÌ. Ero un po 'sconcertato per scoprire che i Multicell non impostano automaticamente l'altezza della riga all'altezza della cella più alta. Ma usare il massimo delle altezze del testo nella riga, ottenuto con questo metodo, è una soluzione abbastanza facile. –

6

Mentre la risposta di Carvell è ottima, TCPDF indica che getStringHeight restituisce l'altezza stimata. Sicuramente la documentazione ci fornisce una tecnica abbastanza completa per ottenere l'altezza esatta che viene fuori come $height. Il motivo per cui non lo usano da soli è un mistero ...

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

Questo è un problema se $ ln = 0, perché quindi $ start_y = $ end_y. Vorrei che memorizzassero il valore di Y, prima di spostare il cursore verso destra. –

+0

Quindi potresti dover usare $ ln = 1 e resettare y e X dopo ogni MultiCell. –

+2

NOTA! Nella versione più recente alcune proprietà sono protette e devi modificare quanto segue: '$ pdf-> h' per' $ pdf-> getPageHeight() ',' $ pdf-> bMargin' per '$ pdf-> getBreakMargin()' e '$ pdf-> tMargin' per' $ pdf-> getMargins() ['top'] ' – TheStoryCoder

1

Uso TCPDF Esempio 20

  1. Calcolo altezze multicella può essere un incubo se le cellule/colonne terminano su pagine diverse.

  2. L'utilizzo di transazioni o di oggetti PDF aggiuntivi può rallentare le cose.

  3. L'utilizzo di funzioni come getNumLines() e getStringHeight() per calcolare l'altezza "stimata" (vedi documenti) prima che le celle vengano stampate non sempre funziona correttamente. Soprattutto se il testo termina poco prima o subito dopo il bordo destro della cella, risultando in righe stampate una sopra l'altra.

I preferiscono la tecnica utilizzata in Example 20 dove si utilizza il valore massimo Y di diverse pagine per calcolare la posizione della nuova riga.

L'esempio stampa solo due colonne, ma ho cambiato la sua funzione principale per poter stampare una matrice di colonne. Ovviamente si potrebbe aggiungere altri dati alla matrice, come il carattere di ogni colonna, bordi, ecc

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