Conheça o Adianti Framework para PHP:

  • Desenvolvimento com componentes;
  • Formulários e datagrids.
  • Versão Web e Desktop (Gtk);
  • Multiplataforma;
  • Desenhe as interfaces;
  • IDE própria (Adianti Studio).
Ver detalhes...

Generating invoices with Glade

Para prosseguir com o download do artigo, é necessário realizar login utilizando o Facebook ou Google. Este procedimento é totalmente seguro e nossa aplicação é certificada por ambos os serviços. Após a autenticação, você será direcionado de volta à nossa página para continuar o download.

Login com Facebook Login com Google

Resumo

Pablo Dall'Oglio Developing complex applications with lots of user's interfaces can give us a big headache if we had to deal all the details in the code, packing and adjusting the size of the widgets by hand. Of course, we can build frameworks for the most common issues like (insert, update, delete and listing windows), but there're always screens that don't fit into these standards. For these ones we can always use glade. Glade is a interface designer built towards Gtk development. So, Glade can be used not only with PHP, but with Python, C, Ruby and others. Glade is a kind of Rapid application Development tool that stores all interface definitions on a XML file that can be used within our application. The only disadvantage when using glade is the parsing time needed to read the glade file at the beggining of applications. Despite this, it makes a lot easier the support for application. Glade Normally, in GTK' programs we use to design all application's interface by hand, declaring classes, packing widgets inside containers, defining sizes of these widgets and so on. This way of design the application gives a better performance. However, when talking about production and development speed, a graphical tool becomes necessary. For these porpouses, there's Glade, a graphical tool, built exactly to help the developers to design the interface of Gtk' applications (C with Gtk, Python with Gtk, Perl with Gtk or PHP with GTK). This way, we don't need to spend time designing the interface by hand. Instead, we can concentrate on the application's business logic. Glade isn't a development IDE that integrates the interface building and the code editing. It's just a tool to help the interface building, the code must be written independently, in some code editor. Using Glade, we can easily create windows, lists, buttons, boxes and organize them in a clear and simple way. At the end, the “interface” schema will be saved on a file “.glade”, that is a XML file containing the visual structure of the interface. The Main window has the basic options (Open and Save), the Project Options and a list of each project window. The Edit Menu has Cut, Copy and Paste Options and View Menu has options to show/hide the pop-up windows (Palette, Properties, etc). The Palette Every Glade project starts with a Window. The Component that represents the window is GtkWindow and is the first component of Glade's Palette. After the first click on the Windows component, the first window comes up and then we can start using another widgets like GtkHBox (horizontal box), GtkVBox (vertical box), GtkFixed (allows absolute positions), GtkLabel (text label), GtkEntry (input boxes), GtkRadioButton, GtkCheckButton, GtkFrame, GtkImage, GtkComboBox, GtkToolBar and others. The components are separeted in pages (Basic, Additional and Deprecated). The Deprecated widgets include the old Gtk1 widgets (GtkFileSelection, GtkCTree, GtkCList, ...) The Widget Tree The Widget Tree (View Menu => Show Widget Tree) is a very nice tool that shows all the widgets you're using in your application through a hierarquical view. Each widget is a node in this tree. The parent/child relationship represents wich widget is inside another one. It's specially usefull to locate widgets, because many times the widget we want to change the properties is not visible (it's the case for structural widgets like GtkHBox'es and GtkVBox'es). A simple right click on any widget in the tree allows you to select it. Then, you can go the the properties window, that changes its content according to the selected widget. The Project Window The project window (represented here by these two samples below) is the space we have to put the widgets together. In the first window below we have a GtkWindow with a GtkFixed component inside it. The GtkFixed component allows to put the components in absolute coordinates inside the window, like buttons, labels, radiobuttons and checkbuttons. In the second sample window we used a different aproach, putting firstly a GtkVBox (Vertical Box) inside the window, and in the second row of the Vertical Box, we put a GtkHBox (Horizongal Box), with GtkLabel (in the second column) and a GtkEntry (in the fourth column). In the following image, the widgets used to build the interface are highlighted. The Properties Window The Properties window is the place where you'll set the properties of the selected widget. It's content depends on the selected widget. A simple click on any widget on the screen and its content adapts to show the properties of the widget's class. The Properties window has several tabs. The first one, called “Widget” define the basic properties of the selected widget. One important thing is to define the widget's name, because it's through the name that we will get the widget in our application from the glade's project file. Besides, we can define a label and the align for a GtkLabel widget, an stock icon and a label for a GtkButton, the max length or the visibility for a GtkEntry and so on. The second Tab (Packing) depends on the way you pack your widgets (in the image below, we have two modes). When the widget is packed inside a GtkFixed container (that allows absolute coordinates), it'll allow to change the X and Y coordinates. But when packing inside a GtkVBox or GtkHBox container, it'll allow to change another properties like Expand (if the widget will expand within the container's limit) or Fill (if the widget will fill all the containers's area), and so on. The third Tab (Common) contains shared properties among the widgets. Properties like Width, Height, if the widget is visible or not, if the widget can have the focus and so on. The Fourth Tab (Signals) allows to connect the widget signals (clicked, pressed, released in the GtkButton case) to a specific callback. This callback is a method or a function that must be written after, using any code editor. When the widget's signal is emitted, the callback with the given name is executed. There's no need to connect the signals in glade, all the connection can be done after, in the code. The Glade XML File When you save your file in Glade, it generates a simple XML file containing the all the visual structure of the project. It contains the widgets's names, properties like dimensions, labels, icons, how each widget will be packed, each registered callback and more. The XML tags are used to define the widget's properties and the hierarchy to represent the containers and its children. This file now can be used inside the application. Instead of declaring all the interface by hand, instantianting window, buttons, labels and putting some widgets inside another ones, we'll call this XML document through a special class (called GladeXML) that parses all the XML content and make available it's objects to our application (through a method called get_widget()). This way we don't need to create the application's interface by hand. Instead, we just need to know the widget's name we want to retrieve from the Glade's project file. <?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> <!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> <glade-interface> <widget class="GtkWindow" id="window1"> <property name="visible">True</property> <property name="title" translatable="yes">window1</property> <property name="type">GTK_WINDOW_TOPLEVEL</property> <property name="window_position">GTK_WIN_POS_NONE</property> <property name="modal">False</property> <property name="resizable">True</property> <property name="destroy_with_parent">False</property> <property name="decorated">True</property> <property name="skip_taskbar_hint">False</property> <property name="skip_pager_hint">False</property> <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> <property name="focus_on_map">True</property> <child> <widget class="GtkVBox" id="vbox1"> <property name="visible">True</property> <property name="homogeneous">False</property> <property name="spacing">0</property> <child> <placeholder/> </child> <child> <widget class="GtkHBox" id="hbox2"> <property name="visible">True</property> <property name="homogeneous">False</property> <property name="spacing">0</property> <child> <widget class="GtkLabel" id="label1"> <property name="visible">True</property> <property name="label" translatable="yes"> Code: </property> <property name="use_underline">False</property> <property name="use_markup">False</property> <property name="justify">GTK_JUSTIFY_LEFT</property> <property name="wrap">False</property> <property name="selectable">False</property> <property name="xalign">0.5</property> <property name="yalign">0.5</property> <property name="xpad">0</property> <property name="ypad">0</property> <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> <property name="width_chars">-1</property> <property name="single_line_mode">False</property> <property name="angle">0</property> </widget> <packing> <property name="padding">0</property> <property name="expand">False</property> <property name="fill">False</property> </packing> </child> </widget> <packing> <property name="padding">0</property> <property name="expand">True</property> <property name="fill">True</property> </packing> </child> <child> <placeholder/> </child> </widget> </child> </widget> </glade-interface> The first sample In our first practical sample, we'll design a simple window (GtkWindow) with a vertical box inside (GtkVBox). In the first position of vertical box, we'll place a text label with the text “Type your Name” in bold. In the second position, we'll place an input entry called “name”. In the third position, we'll place a button called “Print”, and in the fourth position, we'll place another text label, called “result”. The program works this way: the user can type anything inside the “name” input text. When he clicks on “Print” button, the text “Hello <name>” is set in the result text label, where <name> is the expression typed in the “name” field. This sample isn't object oriented. It's bad, but it's very simple to explain how glade works this manner. Pay attention on how we reuse the file designed in Glade. Using the GladeXML class, we receive an object (called $glade in this sample). The glade object offers a special method called get_widget(). Through get_widget() we can retrieve the objects from Glade file just giving its name as parameter. The result is the instantiated object. So we can do anything with the object, changing its properties, connecting its signals, showing or hiding and so on. <?php # instantiate the glade object $glade  = new GladeXML('exemplo1.glade'); # get the name input entry $name   = $glade->get_widget('name'); # get the print button $print  = $glade->get_widget('print'); # get the result label $result = $glade->get_widget('result'); # connect the print button $print->connect_simple('clicked', 'onPrint'); function onPrint() {     global $name, $result;          # get the typed text     $text = $name->get_text();     # set the result label     $result->set_text("Hello {$text}"); } Gtk::Main(); ?> The Application The Main application proposed in this article is an Invoice Generator. We'll basically use Glade to design the interface, explaining step by step through the screenshots, highlighting the widgets we use. After, we'll list the source code. The Invoice code will use basically the glade interface and the FPDF library to generate the Invoice in PDF format. FPDF is an open source library which allows to generate PDF files with pure PHP. FPDF has lots of facilities to draw images, lines, text and son on. That's why it's our choice to use along with our PHP-GTK sample. The Main Window To start designing the main window we use the first component of palette. As soon as you click on the GtkWindow button the first window is shown up. Then we can start placing a widget inside the window. A GtkWindow accepts just one widget inside it, so you must care about what kind of widget to add inside a window. It must be a container. Vertical boxes (GtkVBox) and Horizontal boxes (GtkHBox), besides frames (GtkFrame) and others widgets accepts widgets inside it because they inherit from GtkContainer base class. In our application we'll use the GtkFixed widget, because it accepts the widgets in absolute coordinates in its area. Adding a Image One of the first things to do is to add a logo to our application. Using the button highlighted in the left side (palette screenshot), that represents the GtkImage widget, we can load almost all popular formats (png, jpg, xpm and others). GtkImage also supports transparency what's good. You have just to click on the GtkImage component at the Palette, and click again at the place inside the GtkFixed area you want to put it. Then you can change the “icon” property of the GtkImage to select a image from your filesystem. There's no much to do with an image after adjust its position on the screen, that you can achieve using the mouse or editing the propertis of the “Packing” tab. Adding a Label After inserting a image, we can start with text labels. One of the great news in Gtk2 is the possibility to use a markup language to define fonts, sizes, color and styles. To put a GtkLabel component inside our GtkFixed area we just need to click at the GtkLabel component and after this, click at the place we want it. Then, we can start editing its “Label” property, where we can use the new markup language (remember to turn on the button “Use Markup”). Using the syntax <span font_desc=”Times Bold Italic 10”> we define the font attributes. Using <span foreground=red> we define the color, and using <b>, <i> or <u> we define the style (bold, italic or underline). This markup language is very close to HTML. A good reference is (http://developer.gnome.org/doc/API/2.0/pango/PangoMarkupFormat.html) that documents the pango API with every keywords we can use. Adding an Entry We will not document every steps of designing the application because many of them are repetible. The next one ilustrates how to create an input entry. In this case, we'll use the GtkEntry widget and calling it “customerName”. The “name” property of the widget is the most important thing, because it's through the widget's name that we'll retrieve the object from glade file (in our code). We can also adjust the input entry absolute position (X, Y) using the packing properties and its size using the common properties. Adding a Button Now, let's place some action buttons in our interface. The first one, will use a stock image (+add) and will be called “addButton”. Through this name we'll retrieve this object from the code. This first button will be responsible by read the product information like code, description, quantity and price and add these data to a list of items (that will be created in the next step). The interface we'll have another action buttons, but there's no need to replicate the explanation for each button. We'll explain all the interface ahead giving names for all the widgets on the window. Adding the TreeView Finally, we'll add a GtkTreeView widget. GtkTreeView is responsible for showing both lists and trees. One of the most important features of GtkTreeview is the complete separation from the data model and its visualization. So Glade can place an empty GtkTreeView on the screen, but the data model can be only created inside our code, using the structures (strings, arrays) of the choosen language (php in our case). This GtkTreeView you see in the Glade is just a sample. We'll create the columns inside the application. The Main Interface That's our interface. In the header, the logo of Gnome Foundation, the title and the address, formatted using the new pango markup language. Also we have the Invoice Number and the Date. In the Customer section we have the Customer's name, address and Telephone. In the Product section we have the Product's Code, Description, Quantity and Price, besides the “add” button wich read the product's data and add it to the items list. In the bottom right corner we have SubTotal, taxes, the shipping costs and the Total field that are automatically calculated. The taxes are 5% and the shipping cost is $ 0,40 per product unit. Inside our code, we will reference all the time the name of the widgets. As we didn't document all the steps of interface building, we'll see the application interface with each widget and its name (in red). We'll retrieve these objects through the code through their names. So it's very important to remember these names. The Main File It's our main file. Instead of doing a procedural code, we'll write an object oriented one. First of all, our Invoice will inherit from GladeXML class. GladeXML is responsible by load the interface designed by glade and instantiate the objects through the get_widget() method. In the constructor method, we'll connect the buttons the their callbacks and create the data model for the items list. It's a pain to retrieve an object from glade file everytime we need it. So to make the life easier we'll use a new resource from PHP5, the __get() method. The method __get() intercepts everytime someone try to access an object property. In our case, everytime the property doesn't exist, php goes to glade and instantiate the object through get_widget() method. This way, when we need to access the customerName object from glade interface we just need to use $this->customerName, the same when we need to use the “save” button, just using $this->saveButton. Whenever the users type the product information and clicks on the button “add” the method addItem() is exeucted, adding the product information to the item list. Whenever the user clicks on the button “save”, the saveInvoice() method is executed and the user is asked by the filename through the GtkFileChooserDialog class and the class InvoicePdf is instantiated, generating the PDF file. This class is responsible by the PDF creation. When the invoice is generated, the invoice's number is incremented and the list of items is cleared for the next invoice. Invoice.php <? /** * Class Invoice * Deals with interface and starts the invoice generation */ final class Invoice extends GladeXML {     private $invoice;             // invoice object     private $taxes        = 0.05; // 5%     private $shipping_fee = 0.4;  // $ 0.4 per unity     private $amount       = 0;    // quantity of items          /**      * Constructor Method      * Reads the interface and connects the signals      */     public function __construct()     {         // Call GladeXML Constructor Method         parent::__construct('interface.glade');         // define the standard for calculations         setlocale(LC_ALL, 'POSIX');                  // Connect the action buttons         $this->saveButton->connect_simple('clicked', array($this, 'saveInvoice'));         $this->clearButton->connect_simple('clicked', array($this, 'clearItems'));         $this->addButton->connect_simple('clicked', array($this, 'addItem'));                  // define the invoice date as today         $this->invoiceDate->set_text(date("Y-m-d"));                  // creates the model to store the data         $this->model = new GtkListStore(Gtk::TYPE_STRING, Gtk::TYPE_STRING,                                         Gtk::TYPE_STRING, Gtk::TYPE_STRING);                                            $this->itemsList->set_model($this->model);                  // creates the columns         $column1 = new GtkTreeViewColumn();         $column2 = new GtkTreeViewColumn();         $column3 = new GtkTreeViewColumn();         $column4 = new GtkTreeViewColumn();                  // create the cell renderers         $cell_renderer1 = new GtkCellRendererText();         $cell_renderer2 = new GtkCellRendererText();         $cell_renderer3 = new GtkCellRendererText();         $cell_renderer4 = new GtkCellRendererText();                  // pack one cell renderer per column         $column1->pack_start($cell_renderer1, true);         $column2->pack_start($cell_renderer2, true);         $column3->pack_start($cell_renderer3, true);         $column4->pack_start($cell_renderer4, true);                  // define the indexes         $column1->set_attributes($cell_renderer1, 'text', 0);         $column2->set_attributes($cell_renderer2, 'text', 1);         $column3->set_attributes($cell_renderer3, 'text', 2);         $column4->set_attributes($cell_renderer4, 'text', 3);                  $cell_renderer2->set_property('width', 200);                  $this->itemsList->append_column($column1);         $this->itemsList->append_column($column2);         $this->itemsList->append_column($column3);         $this->itemsList->append_column($column4);                  // define the titles of the columns         $column1->set_title('Code');         $column2->set_title('Description');         $column3->set_title('Quantity');         $column4->set_title('Price');              }          /**      * Method __get()      * Returns a glade object whenever a property doesn' exist      */     public function __get($property)     {         // retrieve the object from glade         return parent::get_widget($property);     }     /**      * Method addItem()      * Add an Item to the list      */     public function addItem()     {         $product   = array(); // initiates the product array                  // get the data from screen         $product[] = $this->productCode->get_text();         $product[] = $this->productDescription->get_text();         $product[] = $this->productQuantity->get_text();         $product[] = $this->productPrice->get_text();                  // add the product to the model                  $this->model->append($product);         // increments the amount         $this->amount += $this->productQuantity->get_text();                  // calculates the subtotal         $this->subTotal->set_text($this->subTotal->get_text() +                                   $this->productPrice->get_text());                                            // calculates the shipping cost         $this->Shipping->set_text($this->amount * $this->shipping_fee);                  // calculates the taxes         $this->Taxes->set_text($this->subTotal->get_text() *                                $this->taxes);         // calculates the total         $this->Total->set_text($this->subTotal->get_text() +                                $this->Shipping->get_text() +                                $this->Taxes->get_text());                  // clear the product data         $this->productCode->set_text('');         $this->productDescription->set_text('');         $this->productQuantity->set_text('');         $this->productPrice->set_text('');         // focuses the product code         $this->window->set_focus($this->productCode);     }          /**      * Method clearItems()      * clear the Items list      */     public function clearItems()     {         // clear the items         $this->model->clear();         $this->amount = 0;         // clear the subtotals,...         $this->subTotal->set_text(0);         $this->Shipping->set_text(0);         $this->Taxes->set_text(0);         $this->Total->set_text(0);         // increments the invoice's number         $this->invoiceNumber->set_text($this->invoiceNumber->get_text() + 1);     }          /**      * Method saveInvoice()      * Generate the invoice      */     public function saveInvoice()     {         // include our class for Invoice Generation         include_once('InvoicePdf.class.php');                  // instantiates the invoice object         $this->invoice = new InvoicePdf();         // define the invoice's number and date         $this->invoice->setNumber($this->invoiceNumber->get_text());         $this->invoice->setDate($this->invoiceDate->get_text());                  // creates the customer object         $customer->name    = $this->customerName->get_text();         $customer->address = $this->customerAddress->get_text();         $customer->phone   = $this->customerPhone->get_text();         // define the invoice's customer         $this->invoice->setCustomer($customer);                  // loop all the products         $iter = $this->model->get_iter_first();         while ($iter)         {             $code       = $this->model->get_value($iter, 0);             $description= $this->model->get_value($iter, 1);             $quantity   = $this->model->get_value($iter, 2);             $price      = $this->model->get_value($iter, 3);                          // add the product data to our invoice's object             $this->invoice->addItem($code, $description, $quantity, $price);                          $iter = $this->model->iter_next($iter);         }                  // define the footer information         $this->invoice->setFooter($this->subTotal->get_text(),                                   $this->Taxes->get_text(),                                   $this->Shipping->get_text(),                                   $this->Total->get_text());                  // ask the user to save the file         $dialog = new GtkFileChooserDialog('Saving...', NULL,                       Gtk::FILE_CHOOSER_ACTION_SAVE,                       array(Gtk::STOCK_OK,     Gtk::RESPONSE_OK,                             Gtk::STOCK_CANCEL, Gtk::RESPONSE_CANCEL));         $response = $dialog->run();                  if ($response == Gtk::RESPONSE_OK)         {             // generates the invoice, passing the filename             $this->invoice->Generate($dialog->get_filename());         }         $dialog->destroy();         $this->clearItems();     } } // instantiate the interface $application = new Invoice; Gtk::Main(); ?> The PDF Generation This class is responsible by PDF creation. It instantiates the FPDF class and make available some methods like setCustomer() that defines the customer's data (name, address and phone), setFooter() that defines the footer's information (subtotal, shipping costs, taxes and total), addItem() that add a product to the list of products and Generate() that reads all the information and draw the PDF document using the FPDF library. After all, it raises the favorite viewer to show the result on the screen. InvoicePdf.class.php <? /** * Class InvoicePdf * Genereates the Invoice * with the FPDF library */ class InvoicePdf {     private $pdf;       // Instance of FPDF class     private $number;    // Invoice Number     private $customer;  // Customer Object     private $items;     // array with the products     /**      * Constructor Method      * Creates the PDF document      */     public function __construct()     {         // Define the fonts directory of FPDF         define('FPDF_FONTPATH', getcwd() . '/fpdf/font/');         // Load the FPDF library         include_once 'fpdf/fpdf.php';                  // creates a new PDF document         $this->pdf = new FPDF('P', 'pt', array(596,540));         $this->pdf->SetMargins(2,2,2); // define margins              }     /**      * Method setNumber      * Define the Invoice Number      */     public function setNumber($number)     {         $this->number = $number;  // define the invoice's number     }     /**      * Method setDate      * Define the Invoice Date      */     public function setDate($date)     {         $this->date = $date;  // define the invoice's date     }     /* Method setCustomer      * Store an object with the customer's data      */     public function setCustomer($object)     {         $this->customer->name    = $object->name;         $this->customer->address = $object->address;         $this->customer->phone   = $object->phone;     }     /* Method setFooter      * Define the totals, subtotals, taxes...      */     public function setFooter($subTotal, $Taxes, $Shipping, $Total)     {         $this->subTotal = $subTotal;         $this->Taxes    = $Taxes;         $this->Shipping = $Shipping;         $this->Total    = $Total;     }     /* Method addItem      * Add the product's data to the array of items      */     public function addItem($code, $description, $quantity, $price)     {         $this->items[] = array($code, $description, $quantity, $price);     }     /* Method Generate      * Generates the PDF Document and saves it      * to the file passed as the first parameter      */     public function Generate($arquivo)     {         // create an empty page         $this->pdf->AddPage();         $this->pdf->Ln();         $image  = 'gnome.jpg';         $size   = getimagesize($image);         $width  = $size[0];         $height = $size[1];                  // draw the image at the x=7,y=7         $this->pdf->Image($image, 7, 7, $width, $height);                  $this->pdf->SetLineWidth(1);         $this->pdf->SetFillColor(247,247,247);         $this->pdf->Rect(434,24,147,57, 'DF');                  // print Company's name and address         // print invoice's number and date         $this->pdf->SetTextColor(0,0,0);         $this->pdf->SetFont('Times','B',28);         $this->pdf->Text(500,48, $this->number);         $this->pdf->Text(140,48, 'Gnome Foundation');         $this->pdf->SetFont('Times','B',16);         $this->pdf->Text(500,68, $this->date);         $this->pdf->SetFont('Arial','B',12);         $this->pdf->Text(440,47, 'Number');         $this->pdf->Text(460,67, 'Date');                  // print company's address         $this->pdf->SetTextColor(0, 0, 0);         $this->pdf->SetFont('Arial','I',12);         $this->pdf->Text(140, 70, '8 Cambridge Center');         $this->pdf->Text(140, 84, 'Floor 5');         $this->pdf->Text(140, 98, 'Cambridge, MA 02142');         $this->pdf->Text(140,112, 'USA');                  $this->pdf->SetLineWidth(2);         $this->pdf->SetFillColor(247,247,247);         $this->pdf->Rect(7,146,580,47, 'DF');         $this->pdf->SetFont('Arial','B',12);         $this->pdf->SetTextColor(0,0,0);                  // print customer's data         $this->pdf->Text(10,164, "Customer:  {$this->customer->name}");         $this->pdf->Text(10,180, "Address:   {$this->customer->address}, Phone: {$this->customer->phone}");                  $this->pdf->SetFillColor(255,255,255);         $this->pdf->Rect(7,204,580,200, 'DF');         $this->pdf->SetFillColor(222,222,222);         $this->pdf->Rect(7,204,580,20, 'DF');                  // print the product's headers         $this->pdf->Text(14,  218, 'Code');         $this->pdf->Text(100, 218, 'Description');         $this->pdf->Text(400, 218, 'Quantity');         $this->pdf->Text(500, 218, 'Price');                  $this->pdf->SetFont('Courier','B',12);         $row=244;         if ($this->items)         {             // print product by product             foreach ($this->items as $item)             {                 $this->pdf->Text(14, $row, $item[0]);                 $this->pdf->Text(100,$row, $item[1]);                 $this->pdf->Text(400,$row, $item[2]);                 $this->pdf->Text(500,$row, 'U$ ' . number_format($item[3], 2));                 $row += 16;             }         }                  $this->pdf->SetFillColor(247,247,247);         $this->pdf->Rect(7,416,580,80, 'DF');                  $this->pdf->SetFont('Courier','B',14);                  // print the invoice's footer with totals and fees...         $this->pdf->Text(440, 434, 'SubTotal: ' . $this->subTotal);         $this->pdf->SetTextColor(222, 0, 0);         $this->pdf->Text(440, 448, 'Taxes:    ' . $this->Taxes);         $this->pdf->SetTextColor(0, 128, 0);         $this->pdf->Text(440, 462, 'Shipping: ' . $this->Shipping);         $this->pdf->SetTextColor(0, 0, 0);         $this->pdf->Text(440, 476, 'Total:    ' . $this->Total);         $this->pdf->Output($arquivo);                  exec("gpdf $arquivo &"); // opens the PDF with your favorite viewer!     } } ?> The Invoice And here it's the result. Our Invoice. A PDF file containing the company's logo, name and address at the left top corner, the invoice's number and date at the right top corner with a rectangle around. Below, we have the Customer info (Name, Address and Phone) inside a gray rectangle, and in the middle of the Invoice we have the Product's list (Code, Description, Quantity and Price) with a header with the column's names inside a dark gray rectangle. At the footer we have the SubTotal, Taxes, Shipping and the Grand Total with the same colors used in Glade interface. PS: Pablo Dall'Oglio (pablo@dalloglio.net) is author of the first book about PHP-GTK of the world. He is also author of Agata Report Tulip Editor and Coordinator of GNUTeca project (an open source software for library management).