Ankündigung

Einklappen
Keine Ankündigung bisher.

Layoutboxen zeichnen, welches Pattern

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Layoutboxen zeichnen, welches Pattern

    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:
    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);
    ?>
    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):
    PHP-Code:
    <?php
    $decorator 
    = new MyLayout_Renderer_Imagick($layout);
    $decorator->render();

    // oder 

    $decorator = new MyLayout_Renderer_Pdf($layout);
    $decorator->render();
    ?>
    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.
    "[URL="http://www.youtube.com/watch?v=yMAa_t9k2VA&feature=youtu.be&t=25s"]Mein Name ist Lohse, ich kaufe hier ein.[/URL]"

  • #2
    Ist das Problem unklar oder habt ihr alle keine Ahnung?
    "[URL="http://www.youtube.com/watch?v=yMAa_t9k2VA&feature=youtu.be&t=25s"]Mein Name ist Lohse, ich kaufe hier ein.[/URL]"

    Kommentar


    • #3
      1. Ein Interface, das so ähnlich aussieht wie java.awt.Graphics.
      2. Bei Bedarf Implementierungen dieses Interfaces, die Bilder mit Imagik oder PDF-Dokumente erzeugen.
      3. Ein Renderer, der das Graphics-Interface abfragen kann (Seitenabmessungen, Raumbedarf gerenderter Texte, etc) und das Layout in Anweisungen an das Graphics-Interfaces übersetzt.

      Den Renderer würde ich zustandslos gestalten:
      PHP-Code:
      interface Graphics {
        
      /**
         * @returns string A byte stream representing the rendered data
         */
        
      public function getRenderedData();

        
      // TODO: add methods for rendering and querying
      }

      class 
      Renderer {
        public function 
      render(MyLayout $theLayoutGraphics $theGraphics) {
          
      // TODO: implement
        
      }

      Meinungen, die ich geäußert habe, sind nicht notwendigerweise meine eigenen. Abweichungen von der deutschen Rechtschreibung unterliegen dem Urheberrecht, dürfen aber unter den Bedingungen von verwendet werden

      Kommentar


      • #4
        Aber wie übergebe ich meine "Boxen" an eine Klasse mit diesem Interface? Ich meine eigentlich muss ich ja das gesamte verschachtelte Boxenmodell übergestülpt bekommen mit Klassen, die dieses Interface implementieren, oder?
        "[URL="http://www.youtube.com/watch?v=yMAa_t9k2VA&feature=youtu.be&t=25s"]Mein Name ist Lohse, ich kaufe hier ein.[/URL]"

        Kommentar


        • #5
          Zitat von Chriz Beitrag anzeigen
          Aber wie übergebe ich meine "Boxen" an eine Klasse mit diesem Interface?
          Überhaupt nicht. Der Renderer holt sich die Boxen vom Layout und erzeugt daraus Low-Level-Zeichenoperationen für das Graphics-Interface.
          Meinungen, die ich geäußert habe, sind nicht notwendigerweise meine eigenen. Abweichungen von der deutschen Rechtschreibung unterliegen dem Urheberrecht, dürfen aber unter den Bedingungen von verwendet werden

          Kommentar


          • #6
            Das heißt ich arbeite in Renderer::render() mit Rekursion, durchlaufe also die oberste Stufe ($box), suche Kindelemente (Text, Hr, Image, $box2) und führe sie rekursiv auf?

            Was nun, wenn das Bild widererwartend viel zu groß (vma. zu hoch) für die Zeichenfläche ist? Dann habe ich den Content ja schon soweit gerendert, dass es zu spät ist, alles zurückzunehmen und entsprechend darauf zu reagieren. Das Bild ist vielleicht eine schlechtes Beispiel. Sagen wir die Zeichenfläche ist nicht breit genug, dass Copyright und Stand-der-Datenbank nebeneinander dargestellt werden können. Ich müsste also umbrechen. Wenn ich das Copyright aber schon komplett gezeichnet habe und dann merke, für Stand-der-Datenbank habe ich nurnoch 5px übrig, dann muss ich ja eine vorherige Höhe/Breite für alle Boxen berechnen um sicherzustellen, dass jeder genug Platz bekommt.
            "[URL="http://www.youtube.com/watch?v=yMAa_t9k2VA&feature=youtu.be&t=25s"]Mein Name ist Lohse, ich kaufe hier ein.[/URL]"

            Kommentar

            Lädt...
            X