Hallo,
ich habe mit Imagick vor einiger Zeit Diagramme verschiedenster Art (Balken, Kreis, Alterspyramiden, ..) erzeugt und bin nun beim Erzeugen von PDFs (über mehrere Seiten) auf ein ähnliches Problem gestossen. Wie positioniere ich einen definierten Inhalt (kommend z.B. aus der Datenbank) in einem begrenzt verfügbaren Bereich (Zeichenfläche in Imagick, Seite eines PDFs, ..)? Man kann das ganze natürlich pixelbasiert angehen. Sprich ich merke mir, wo ich gerade bin und fange dann an, meinen Inhalt hineinzuschreiben. Das ist nicht wiederverwendbar, aber schnell heruntergeschrieben, solange nicht mittendrin mal Änderungen eingefügt werden müssen.
Also mein Diagrammlayout sieht beispielsweise so aus, dass oben links ein Titel steht, darunter eine Begrenzungslinie folgt (~ <hr />), darauf das eigentliche Diagramm folgt und darunter wiederum Copyright (linksbündig) und Stand der Datenbank (rechtsbündig) auf einer Höhe das Diagramm abschließen.
Beim PDF ist das ähnlich, Header (links Firma, rechts Betreff) und Footer (zentriert "Seite #xy#"), dazwischen Content, also Tabellen, Fließtext und dazwischen die Möglichkeit auf eine neue Seite umbrechen zu können.
Ich hab mich dunkel an eine Javavorlesung erinnert, in der eine Art Boxenmodell dafür vorgestellt wurde und zwar mit der Möglichkeit horizontale und vertikale Boxen zu kombinieren. Eine horizontale Box stellt ihren (beliebigen) Inhalt nebeneinander dar, eine vertikale untereinander. Eine freie Verschachtelung beider Boxentypen erlaubt also ein komplett frei zu gestaltendes Layout. Der Vorteil: Beide haben fast identische Eigenschaften (Padding, Background, Margin, Höhen und Breitenberechnung, ...).
Nun ich habe das ganze für die PDF-Erzeugung (Erweiterung von Zend_Pdf) einmal umgesetzt und das Ergebnis beeindruckt mich. Ich habe es geschafft, dass eine Box als zusammengehörig markiert werden kann (ganz wie in *.docx): "keepTogether", so dass der Inhalt gemeinsam auf eine neue Seite umgebrochen werden kann und das gute ist, ich muss eigentlich nur an einer Klasse entwickeln (Box_Abstract). Das heißt wenn ich definiere, dass es Margin oder Padding oder ein Hintergrundbild gibt, steht das sofort allen Elementen zur Verfügung. Innerhalb kürzester Zeit konnte ich das relativ schwache Zend_Pdf zu einem sehr netten Werkzeug erweitern.
Nun, die Klassen die ich dafür geschrieben habe (BoxVertical, BoxHorizontal, BoxText, BoxImage, BoxTable, ..) sind nur für das PDF zu gebrauchen. Mir kam in den Sinn, ob man nicht eine allgemeinere Implementierung ermöglichen könnte, so dass ich das ganze einem Renderer, z.B. für Imagick oder eben für Zend_Pdf übergebe. Nur wie mache ich das Software-Design technisch? Sind das Decorator? Welches Entwurfsmuster hilft mir hier?
Also stellt euch vor, ich habe dieses Konstrukt erschaffen:
Es ist völlig entkoppelt von einer Darstellung, bildet erstmal nur im PHP-Code einen Inhalt ab, wie ich es für das Diagramm oben beschrieben habe.
An der Umsetzung für ein Rendering scheitere ich jetzt. Wie entkopple ich die Abbildung des Layouts vom Vorgang des Renderns (in PDF, Imagick, .. was auch immer)? Bzw. wie ist mein Renderer implementiert?
Ich dachte an so eine Art Decorator (wie man es von Zend_Form kennt):
Aber hier verschließt sich mir nicht, wie meine render()-Methode praktisch aussehen würde. Müsste ich darin alle Objekte ($box, $box2) nicht mit einer speziellen Decorator-Klasse "überstülpen"? Dann muss ich ja erst rekursiv durch alle Objekte laufen, sie durch einen passenden Decorator ersetzen (MyDecorator_BoxVertical, ...) um dann noch einmal durchzulaufen, damit das ganze auch mal endlich ein Bild oder PDF wird.
ich habe mit Imagick vor einiger Zeit Diagramme verschiedenster Art (Balken, Kreis, Alterspyramiden, ..) erzeugt und bin nun beim Erzeugen von PDFs (über mehrere Seiten) auf ein ähnliches Problem gestossen. Wie positioniere ich einen definierten Inhalt (kommend z.B. aus der Datenbank) in einem begrenzt verfügbaren Bereich (Zeichenfläche in Imagick, Seite eines PDFs, ..)? Man kann das ganze natürlich pixelbasiert angehen. Sprich ich merke mir, wo ich gerade bin und fange dann an, meinen Inhalt hineinzuschreiben. Das ist nicht wiederverwendbar, aber schnell heruntergeschrieben, solange nicht mittendrin mal Änderungen eingefügt werden müssen.
Also mein Diagrammlayout sieht beispielsweise so aus, dass oben links ein Titel steht, darunter eine Begrenzungslinie folgt (~ <hr />), darauf das eigentliche Diagramm folgt und darunter wiederum Copyright (linksbündig) und Stand der Datenbank (rechtsbündig) auf einer Höhe das Diagramm abschließen.
Beim PDF ist das ähnlich, Header (links Firma, rechts Betreff) und Footer (zentriert "Seite #xy#"), dazwischen Content, also Tabellen, Fließtext und dazwischen die Möglichkeit auf eine neue Seite umbrechen zu können.
Ich hab mich dunkel an eine Javavorlesung erinnert, in der eine Art Boxenmodell dafür vorgestellt wurde und zwar mit der Möglichkeit horizontale und vertikale Boxen zu kombinieren. Eine horizontale Box stellt ihren (beliebigen) Inhalt nebeneinander dar, eine vertikale untereinander. Eine freie Verschachtelung beider Boxentypen erlaubt also ein komplett frei zu gestaltendes Layout. Der Vorteil: Beide haben fast identische Eigenschaften (Padding, Background, Margin, Höhen und Breitenberechnung, ...).
Nun ich habe das ganze für die PDF-Erzeugung (Erweiterung von Zend_Pdf) einmal umgesetzt und das Ergebnis beeindruckt mich. Ich habe es geschafft, dass eine Box als zusammengehörig markiert werden kann (ganz wie in *.docx): "keepTogether", so dass der Inhalt gemeinsam auf eine neue Seite umgebrochen werden kann und das gute ist, ich muss eigentlich nur an einer Klasse entwickeln (Box_Abstract). Das heißt wenn ich definiere, dass es Margin oder Padding oder ein Hintergrundbild gibt, steht das sofort allen Elementen zur Verfügung. Innerhalb kürzester Zeit konnte ich das relativ schwache Zend_Pdf zu einem sehr netten Werkzeug erweitern.
Nun, die Klassen die ich dafür geschrieben habe (BoxVertical, BoxHorizontal, BoxText, BoxImage, BoxTable, ..) sind nur für das PDF zu gebrauchen. Mir kam in den Sinn, ob man nicht eine allgemeinere Implementierung ermöglichen könnte, so dass ich das ganze einem Renderer, z.B. für Imagick oder eben für Zend_Pdf übergebe. Nur wie mache ich das Software-Design technisch? Sind das Decorator? Welches Entwurfsmuster hilft mir hier?
Also stellt euch vor, ich habe dieses Konstrukt erschaffen:
PHP-Code:
<?php
$layout = new MyLayout();
$box = $layout->getBoxVertical();
$box->addText("Überschrift");
$box->addHr();
$box->addImage("my-chart.png");
$box2 = $layout->getBoxHorizontal();
$box2->addText("Copyright 2010");
$box2->addText("Stand der DB: heute");
$box->add($box2);
$layout->add($box);
?>
An der Umsetzung für ein Rendering scheitere ich jetzt. Wie entkopple ich die Abbildung des Layouts vom Vorgang des Renderns (in PDF, Imagick, .. was auch immer)? Bzw. wie ist mein Renderer implementiert?
Ich dachte an so eine Art Decorator (wie man es von Zend_Form kennt):
PHP-Code:
<?php
$decorator = new MyLayout_Renderer_Imagick($layout);
$decorator->render();
// oder
$decorator = new MyLayout_Renderer_Pdf($layout);
$decorator->render();
?>
Kommentar