FPDF-Tutorial: Blocksatz und Fließtext

FPDF-Tutorial: Blocksatz und Fließtext
Von Lars Ebert am 19.08.13, 11:00
Kategorien: PHP, Programmieren and Tutorials

Im ersten Artikel dieser Serie haben wir angefangen, eine Rechnung automatisch mit PHP zu erstellen. Dazu benutzen wir FPDF. Du weißt jetzt schon, wie man grundsätzlich Text auf die Rechnung bringt. Als nächstes werden wir uns anschauen, wie wir Fließtext auf die Rechnung schreiben können. Im nächsten Artikel werden wir damit beginnnen, die Rechnungsposten anzuzeigen.

Artikelserie: FPDF-Tutorial - PDF-Generierung mit PHP

Dieser Artikel ist Teil einer mehrteiligen Artikelserie. Lies dir auch die restlichen Teile durch!

  1. FPDF-Tutorial: Ein PDF erstellen und Text ausgeben
  2. FPDF-Tutorial: Blocksatz und Fließtext
  3. FPDF-Tutorial - Rechnungs-Posten und mehrspaltiger Text

Schritt 7: Eine Methode für Absätze erstellen

Später werden wir aus der Datei »index.php« heraus Textinhalte auf die Rechnung bringen wollen. Dazu erstellen wir uns jetzt eine neue Methode Paragraph, welche dafür sorgt, dass im PDF Absätze entstehen. Wir beginnen damit, zunächst den Text zu übergeben und dann die Methode selbst zu schreiben:

<?php
	
	$items = array(
		array(
			'name' => 'Produkt 1',
			'description' => 'Lorem ipsum ad qui amet dolore, vitae cetero quaerendum mel ea. Facilis fastidii duo no.',
			'unitprice' => 5.2,
			'quantity' => 3
		),
		array(
			'name' => 'Produkt 2',
			'description' => 'Lorem ipsum ad qui amet dolore, vitae cetero quaerendum mel ea. Facilis fastidii duo no.',
			'unitprice' => 1,
			'quantity' => 9
		),
		array(
			'name' => 'Produkt 3',
			'description' => 'Lorem ipsum ad qui amet dolore, vitae cetero quaerendum mel ea. Facilis fastidii duo no.',
			'unitprice' => .99,
			'quantity' => 18
		)
	);
	
	$invoiceNumber = date('Ymd') . '-' . rand(0, 100);
	$date = new DateTime();
	
	require_once('invoice.php');
	
	$invoice = new Invoice($invoiceNumber, $date);
	
	$invoice->Paragraph('Sehr geehrte Frau Mustermann,

vielen Dank für Ihren Einkauf bei Advitum.de. Dies ist Ihre Rechnung!

Lorem ipsum ad qui amet dolore, vitae cetero quaerendum mel ea. Facilis fastidii duo no. Viris partiendo ius no, alia animal nam at. Feugait imperdiet ius an, no quis facer lucilius vis. Aliquam saperet contentiones ex pro, id idque offendit ius. Fugit suavitate ad eam, ut essent debitis cum.');
	
	$invoice->output();
	
?>

So soll später der Aufruf der Methode aussehen. Nun schreiben wir die Methode:

public function Paragraph($text) {
	$text = explode("\n", $text);
	$this->SetFont('Helvetica', '', 9);
	
	foreach($text as $cell) {
		$cell = trim($cell);
		
		if($cell != '') {
			$this->BlockCell($cell, $this->leftColumnWidth);
		}
		
		$this->ln($this->rythm);
	}
	
	$this->ln($this->rythm);
}

Die Methode Paragraph teilt mittels explode den Text in einzelne Zeilen auf. Die Textgröße wird auf 9 Pt eingestellt.

Nun werden alle Zeilen durchlaufen. Wenn die Zeile nicht leer ist, wird ihr Text an eine weitere Methode BlockCell übergeben, zusammen mit der Breite der linken Spalte. Diese Methode gibt es momentan noch nicht, aber wir werden sie jetzt erstellen.

Schritt 8: Blocksatz in FPDF

Die Theorie hinter dem Blocksatz ist sehr einfach. Zunächst ermitteln wir, wie viele Wörter in die erste Zeile passen. Danach errechnen wir, wie viel Weißraum am Ende übrig bleibt und teilen diesen gleichmäßig auf alle Leerzeichen in der Zeile auf.

private function BlockCell($text, $width) {
	$minSpace = 0.05 * $this->FontSizePt;
	$words = explode(' ', $text);
	
	$lineWords = array();
	$lineWidth = 0;
	while(count($words) > 0) {
		$word = array_shift($words);
		$wordWidth = $this->GetStringWidth($word);
		if($lineWidth + $wordWidth + $minSpace >= $width) {
			$deltaWidth = $width - $lineWidth;
			$deltaSpace = $deltaWidth / (count($lineWords) - 1);
			
			foreach($lineWords as $lineWord) {
				$lineWordWidth = $this->GetStringWidth($lineWord);
				$this->Cell($lineWordWidth + $minSpace + $deltaSpace, 0, $lineWord);
			}
			
			$lineWords = array();
			$lineWidth = 0;
			
			$this->ln($this->rythm);
		}
		
		$lineWords[] = $word;
		$lineWidth += $minSpace + $wordWidth;
	}
	
	$this->Cell(0, 0, implode(' ', $lineWords));
}

Zunächst legen wir fest, wie breit ein Leerzeichen mindestens sein muss. Dies ist natürlich abhängig von der Schriftgroße, deshalb berechnen wir den Mindestabstand hier mit .05 * $this->FontSizePt. Anschließend teilen wir den String in einzelne Worte auf.

Jetzt gehen wir dieses Array Wort für Wort durch. Mit der Methode GetStringWidth können wir die Breite ermitteln, die ein Text bei aktueller Schriftart und -Größe einnehmen würde. Dazu nehmen wir die Mindestbreite für ein Leerzeichen. In der Variable $lineWidth summieren wir diese Werte auf. Sobald ein Wort nicht mehr in die aktuelle Zeile passt, halten wir inne, um die aktuellen Wörter in die Zeile zu bringen und anschließend eine neue Zeile anzufangen. Hierbei ermitteln wir in $deltaWidth, wie viel Platz uns am Ende der Zeile noch bleibt. In $deltaSpace rechnen wir diese verbleibende Breite nun noch auf die Anzahl der Leerzeichen herunter. Pro Wort rechnen wir auf die Zellbreite nun die Breite des Leerzeichens an.

Am Ende müssen alle Worte, die keine ganze Zeile einnehmen, linksbündig ausgegeben werden.

Und schon ist unser Blocksatz fertig. Und schon ist unser Blocksatz fertig.

Schritt 9: Unterstützung von UTF-8

Wie Dir in Deiner eigenen Rechnung und auf meinem Screenshot bestimmt aufgefallen ist, werden unsere Umlaute nicht korrekt angezeigt. Das liegt daran, dass FPDF noch nicht mit UTF-8 zurechtkommt. Dies können wir aber ganz schnell beheben. Wir müssen einfach dafür sorgen, dass die Texte von UTF-8 umgewandelt werden in einen Zeichensatz, den FPDF verstehen kann.

private function Encode($text) {
	return mb_convert_encoding($text, "ISO-8859-15", "UTF-8");
}

Die Funktion mb_convert_encoding sorgt dafür, dass der Text von UTF-8 nach ISO-8859-15 konvertiert wird. So können wir später auch noch weitere Sonderzeichen wie zum Beispiel das €-Zeichen darstellen. Diese Methode müssen wir nun überall aufrufen, wo mit dem Text gearbeitet wird. Konkret sind das die beiden Methoden Cell und GetStringWidth.

public function Cell($w, $h=0, $text='', $border=0, $ln=0, $align='', $fill=false, $link='') {
	$text = $this->Encode($text);
	
	$startX = $this->GetX();
	$startY = $this->GetY();
	
	$this->SetY($startY - $this->FontSize / 2 * .6);
	$this->SetX($startX);
	
	parent::Cell($w, $h, $text, $border, $ln, $align, $fill, $link);
	
	$endX = $this->GetX();
	$endY = $this->GetY();
	
	$this->SetY($startY);
	$this->SetX($endX);
}

public function GetStringWidth($text) {
	$text = $this->Encode($text);
	return parent::GetStringWidth($text);
}

In beiden Methoden wandle ich zuerst den Text um, bevor irgendwas sonst geschieht. Die Methode GetStringWidth habe ich weiter nicht verändert, sie ruft nach dem Umwandeln die entsprechende Methode in der Eltern-Klasse auf.

Schaust Du Dir nun Dein PDF erneut an, wirst Du feststellen, dass nun die Sonderzeichen korrekt angezeigt werden können.

Wie geht es weiter?

Im nächsten Artikel werden wir die Posten der Rechnung in das PDF schreiben. Dazu schauen wir uns genauer an, wie wir mehrspaltigen Text mit FPDF generieren können.

Nächster Artikel der Serie

Dieser Artikel ist Teil der Artikelserie »FPDF-Tutorial - PDF-Generierung mit PHP«.

Hier geht es zum nächsten Artikel der Serie: FPDF-Tutorial - Rechnungs-Posten und mehrspaltiger Text