From e28ba4839188307f94293af4e29ed6e774c0a499 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Thu, 15 Jan 2015 14:57:19 +0000
Subject: Exported disassembled content in plain text or HTML format properly.

git-svn-id: svn://svn.gna.org/svn/chrysalide/trunk@455 abbe820e-26c8-41b2-8c08-b7b2b41f8b0a
---
 ChangeLog                    |  19 ++
 src/dialogs/export.c         | 573 +++++++++++++++++++++++++++++++++----------
 src/glibext/gbufferline.c    | 136 ++++++++--
 src/glibext/gbufferline.h    |   2 +-
 src/glibext/gbuffersegment.c | 130 +++++++++-
 src/glibext/gbuffersegment.h |  51 +++-
 src/glibext/gcodebuffer.c    |  72 ++++++
 src/glibext/gcodebuffer.h    |   5 +-
 8 files changed, 831 insertions(+), 157 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index f902da1..b02c2cb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+15-01-15  Cyrille Bagard <nocbos@gmail.com>
+
+	* src/dialogs/export.c:
+	Export disassembled content in plain text or HTML format properly.
+
+	* src/glibext/gbufferline.c:
+	* src/glibext/gbufferline.h:
+	Update the export routines.
+
+	* src/glibext/gbuffersegment.c:
+	Fix a bug in pattern slant (mismatch 'italic' vs 'oblique'). Fix a bug
+	when storing the segment text: only keep the submitted length. Update
+	the export routines.
+
+	* src/glibext/gbuffersegment.h:
+	* src/glibext/gcodebuffer.c:
+	* src/glibext/gcodebuffer.h:
+	Update the export routines.
+
 15-01-14  Cyrille Bagard <nocbos@gmail.com>
 
 	* src/arch/arm/v7/arm.c:
diff --git a/src/dialogs/export.c b/src/dialogs/export.c
index 76cdcfd..5667730 100644
--- a/src/dialogs/export.c
+++ b/src/dialogs/export.c
@@ -24,6 +24,7 @@
 #include "export.h"
 
 
+#include <assert.h>
 #include <fcntl.h>
 #include <malloc.h>
 #include <stdio.h>
@@ -33,6 +34,7 @@
 #include <i18n.h>
 
 
+#include "../common/extstr.h"
 #include "../gtkext/easygtk.h"
 
 
@@ -40,41 +42,28 @@
 /* ------------------------ PARTIE PRINCIPALE DE L'ASSISTANT ------------------------ */
 
 
-/* Conservation des informations utiles */
-struct _export_data
-{
-    int fd;                                 /* Flux ouvert en écriture     */
-    BufferExportType type;                  /* Type d'exportation          */
-
-    bool addr;                              /* Affichage des adresses      */
-    bool code;                              /* Affichage du code binaire   */
-    bool content;                           /* Affichage du contenu humain */
-
-};
-
-
 /* Ferme l'assistant sans dérouler la procédure. */
 static void export_assistant_cancel(GtkAssistant *, gpointer);
 
 /* Ferme l'assistant et déroule la procédure. */
 static void export_assistant_close(GtkAssistant *, GObject *);
 
-/* Assure l'exportation en différé. */
-static bool export_buffer_line(GCodeBuffer *, GBufferLine *, struct _export_data *);
 
 
+/* ----------------------- DEFINITION DU FORMAT D'EXPORTATION ----------------------- */
 
-/* -------------------- DEFINITION DES REGLAGES DE L'EXPORTATION -------------------- */
 
+/* Ajoute le panneau de choix du format d'exportation. */
+static void register_format_panel(GtkAssistant *);
 
-/* Ajoute le panneau de choix du type de sortie. */
-static void register_output_panel(GtkAssistant *);
+/* Réagit un changement du format pour l'exportation. */
+static void on_export_format_changed(GtkComboBox *, GtkAssistant *);
 
-/* Réagit un changement du nom de fichier pour l'exportation. */
-static void on_export_filename_changed(GtkEntry *, GtkAssistant *);
+/* Interdit un champ de texte vide pour les options de texte. */
+static void forbid_text_empty_entry(GtkEntry *, GtkAssistant *);
 
-/* Sélectionne ou non un nouveau fichier de sortie. */
-static void on_filename_browsing_clicked(GtkButton *, GObject *);
+/* Interdit un champ de texte vide pour les options HTML. */
+static void forbid_html_empty_entry(GtkEntry *, GtkAssistant *);
 
 
 
@@ -85,6 +74,21 @@ static void on_filename_browsing_clicked(GtkButton *, GObject *);
 static void register_content_panel(GtkAssistant *);
 
 
+
+/* ------------------------ DEFINITION DE LA SORTIE ATTENDUE ------------------------ */
+
+
+/* Ajoute le panneau de choix du type de sortie. */
+static void register_output_panel(GtkAssistant *);
+
+/* Réagit un changement du nom de fichier pour l'exportation. */
+static void on_export_filename_changed(GtkEntry *, GtkAssistant *);
+
+/* Sélectionne ou non un nouveau fichier de sortie. */
+static void on_filename_browsing_clicked(GtkButton *, GObject *);
+
+
+
 /* ---------------------------------------------------------------------------------- */
 /*                          PARTIE PRINCIPALE DE L'ASSISTANT                          */
 /* ---------------------------------------------------------------------------------- */
@@ -109,7 +113,7 @@ void run_export_assistant(GLoadedBinary *binary, GtkWindow *parent)
     GObject *ref;                           /* Espace de référencement     */
 
     assistant = gtk_assistant_new();
-    gtk_widget_set_size_request(assistant, 450, 300);
+    gtk_widget_set_size_request(assistant, 500, 350);
     gtk_window_set_position(GTK_WINDOW(assistant), GTK_WIN_POS_CENTER);
     gtk_window_set_title(GTK_WINDOW(assistant), _("Export assistant"));
 
@@ -119,8 +123,9 @@ void run_export_assistant(GLoadedBinary *binary, GtkWindow *parent)
     ref = G_OBJECT(assistant);
     g_object_set_data(ref, "binary", binary);
 
-    register_output_panel(GTK_ASSISTANT(assistant));
+    register_format_panel(GTK_ASSISTANT(assistant));
     register_content_panel(GTK_ASSISTANT(assistant));
+    register_output_panel(GTK_ASSISTANT(assistant));
 
     g_signal_connect(G_OBJECT(assistant), "cancel", G_CALLBACK(export_assistant_cancel), NULL);
     g_signal_connect(G_OBJECT(assistant), "close", G_CALLBACK(export_assistant_close), ref);
@@ -145,6 +150,14 @@ void run_export_assistant(GLoadedBinary *binary, GtkWindow *parent)
 
 static void export_assistant_cancel(GtkAssistant *assistant, gpointer data)
 {
+    GObject *support;                       /* Support interne à supprimer */
+
+    support = G_OBJECT(g_object_get_data(G_OBJECT(assistant), "text_options"));
+    if (support != NULL) g_object_unref(support);
+
+    support = G_OBJECT(g_object_get_data(G_OBJECT(assistant), "html_options"));
+    if (support != NULL) g_object_unref(support);
+
     gtk_widget_destroy(GTK_WIDGET(assistant));
 
 }
@@ -165,94 +178,121 @@ static void export_assistant_cancel(GtkAssistant *assistant, gpointer data)
 
 static void export_assistant_close(GtkAssistant *assistant, GObject *ref)
 {
+    GtkComboBox *combo;                     /* Selection du format         */
+    BufferExportType type;                  /* Type d'exportation requise  */
+    buffer_export_context ctx;              /* Contexte à constituer       */
     GtkEntry *entry;                        /* Zone de saisie              */
     const gchar *filename;                  /* Chemin d'accès du fichier   */
-    int fd;                                 /* Descripteur de la sortie    */
-    struct _export_data *export;            /* Informations à faire suivre */
     GtkToggleButton *checkbutton;           /* Coche à retrouver           */
+    bool display[BLC_DISPLAY];              /* Affichage à garantir        */
     GLoadedBinary *binary;                  /* Binaire chargé à parcourir  */
     GCodeBuffer *buffer;                    /* Tampon de code à traiter    */
+    GBufferView *view;                      /* Vue à exporter              */
+    GObject *support;                       /* Support interne à supprimer */
+
+    /* Type d'exportation */
+
+    combo = GTK_COMBO_BOX(g_object_get_data(ref, "format"));
+
+    type = (BufferExportType)gtk_combo_box_get_active(combo);
 
     /* Fichier de sortie */
 
     entry = GTK_ENTRY(g_object_get_data(ref, "filename"));
     filename = gtk_entry_get_text(entry);
 
-    fd = open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
-    if (fd == -1)
+    switch (type)
     {
-        perror("open");
-        return;
+        case BET_TEXT:
+        case BET_HTML:
+            ctx.fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR);
+            if (ctx.fd == -1)
+            {
+                perror("open");
+                return;
+            }
+            break;
+
+        default:
+            assert(false);
+            break;
+
     }
 
-    export = (struct _export_data *)calloc(1, sizeof(struct _export_data));
+    /* Eléments à afficher */
 
-    export->fd = fd;
+    checkbutton = GTK_TOGGLE_BUTTON(g_object_get_data(ref, "physical_off"));
+    display[BLC_PHYSICAL] = gtk_toggle_button_get_active(checkbutton);
 
-    /* Type d'exportation */
+    checkbutton = GTK_TOGGLE_BUTTON(g_object_get_data(ref, "virtual_addr"));
+    display[BLC_VIRTUAL] = gtk_toggle_button_get_active(checkbutton);
 
-    export->type = BET_TEXT;
+    checkbutton = GTK_TOGGLE_BUTTON(g_object_get_data(ref, "binary_code"));
+    display[BLC_BINARY] = gtk_toggle_button_get_active(checkbutton);
 
-    /* Eléments à afficher */
+    /* Options éventuelles */
 
-    checkbutton = GTK_TOGGLE_BUTTON(g_object_get_data(ref, "virtual_addr"));
-    export->addr = gtk_toggle_button_get_active(checkbutton);
+    switch (type)
+    {
+        case BET_TEXT:
+            entry = GTK_ENTRY(g_object_get_data(ref, "text_separator"));
+            ctx.sep = gtk_entry_get_text(entry);
+            if (strcmp(ctx.sep, "\\t") == 0) ctx.sep = "\t";  
+            break;
 
-    checkbutton = GTK_TOGGLE_BUTTON(g_object_get_data(ref, "binary_code"));
-    export->code = gtk_toggle_button_get_active(checkbutton);
+        case BET_HTML:
+            entry = GTK_ENTRY(g_object_get_data(ref, "html_font_name"));
+            ctx.font_name = gtk_entry_get_text(entry);
+
+            entry = GTK_ENTRY(g_object_get_data(ref, "html_bg_color"));
+            ctx.bg_color = gtk_entry_get_text(entry);
+
+            break;
 
-    checkbutton = GTK_TOGGLE_BUTTON(g_object_get_data(ref, "assembly_code"));
-    export->content = gtk_toggle_button_get_active(checkbutton);
+        default:
+            break;
+
+    }
 
     /* Programmation de la tâche */
 
     binary = G_LOADED_BINARY(g_object_get_data(ref, "binary"));
 
     buffer = g_loaded_binary_get_disassembled_buffer(binary);
+    view = g_buffer_view_new(buffer);
 
-    g_buffer_code_scan(buffer, 0, VMPA_MAX, _("Exporting binary lines..."),
-                       (process_line_fc)export_buffer_line, export);
+    g_buffer_view_export(view, &ctx, type, display);
 
-    gtk_widget_destroy(GTK_WIDGET(assistant));
+    /* Conclusion */
 
-}
+    switch (type)
+    {
+        case BET_TEXT:
+        case BET_HTML:
+            close(ctx.fd);
+            break;
 
+        default:
+            break;
 
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : buffer = tampon de code contenant toutes les lignes.         *
-*                line   = ligne dont le contenu est à exporter.               *
-*                export = informations utiles à la manoeuvre.                 *
-*                                                                             *
-*  Description : Assure l'exportation en différé.                             *
-*                                                                             *
-*  Retour      : -                                                            *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-static bool export_buffer_line(GCodeBuffer *buffer, GBufferLine *line, struct _export_data *export)
-{
-    /* Si les traitements sont terminés... */
-    if (line == NULL)
-    {
-        close(export->fd);
-        free(export);
     }
 
-    else
-        g_buffer_line_export(line, export->fd, export->type,
-                             true/* FIX%E */, export->addr, export->code, export->content);
+    g_object_unref(view);
+
+    support = G_OBJECT(g_object_get_data(G_OBJECT(assistant), "text_options"));
+    if (support != NULL) g_object_unref(support);
 
-    return true;
+    support = G_OBJECT(g_object_get_data(G_OBJECT(assistant), "html_options"));
+    if (support != NULL) g_object_unref(support);
+
+    gtk_widget_destroy(GTK_WIDGET(assistant));
 
 }
 
 
 
 /* ---------------------------------------------------------------------------------- */
-/*                      DEFINITION DES REGLAGES DE L'EXPORTATION                      */
+/*                         DEFINITION DU FORMAT D'EXPORTATION                         */
 /* ---------------------------------------------------------------------------------- */
 
 
@@ -260,7 +300,7 @@ static bool export_buffer_line(GCodeBuffer *buffer, GBufferLine *line, struct _e
 *                                                                             *
 *  Paramètres  : assistant = fenêtre à compléter et référencement global.     *
 *                                                                             *
-*  Description : Ajoute le panneau de choix du type de sortie.                *
+*  Description : Ajoute le panneau de choix du format d'exportation.          *
 *                                                                             *
 *  Retour      : -                                                            *
 *                                                                             *
@@ -268,17 +308,15 @@ static bool export_buffer_line(GCodeBuffer *buffer, GBufferLine *line, struct _e
 *                                                                             *
 ******************************************************************************/
 
-static void register_output_panel(GtkAssistant *assistant)
+static void register_format_panel(GtkAssistant *assistant)
 {
     GtkWidget *alignment;                   /* Disposition sur le support  */
     GtkWidget *vbox;                        /* Support principal #1        */
     GtkWidget *hbox;                        /* Support principal #2        */
     GtkWidget *label;                       /* Etiquette d'indication      */
     GtkWidget *combobox;                    /* Sélection du format         */
-    GtkWidget *entry;                       /* Zone de saisie de texte     */
-    GtkWidget *button;                      /* Sélection de fichier        */
-    GLoadedBinary *binary;                  /* Binaire chargé à parcourir  */
-    const char *filename;                   /* Chemin d'accès par défaut   */
+    GtkWidget *options;                     /* Zone d'options              */
+    GtkWidget *content;                     /* Accueil desdites options    */
 
     alignment = qck_create_padded_alignment(8, 8, 8, 8);
 
@@ -295,45 +333,173 @@ static void register_output_panel(GtkAssistant *assistant)
     label = qck_create_label(NULL, NULL, _("Format: "));
     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
 
-    combobox = qck_create_combobox(NULL, NULL, G_CALLBACK(NULL), NULL);
+    combobox = qck_create_combobox(G_OBJECT(assistant), "format", G_CALLBACK(on_export_format_changed), assistant);
     gtk_box_pack_start(GTK_BOX(hbox), combobox, TRUE, TRUE, 0);
 
     gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combobox), _("Simple text"));
+    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combobox), _("HTML"));
 
-    gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0);
-
-    /* Fichier de sortie */
-
-    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
-    gtk_widget_show(hbox);
-    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+    /* Eventuelles options */
 
-    label = qck_create_label(NULL, NULL, _("File: "));
-    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+    options = qck_create_frame(_("<b>Options</b>"), &content, 0, 12, 12, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), options, FALSE, FALSE, 0);
 
-    entry = qck_create_entry(G_OBJECT(assistant), "filename", NULL);
-    gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
-
-    button = qck_create_button(NULL, NULL, "...", G_CALLBACK(on_filename_browsing_clicked), assistant);
-    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+    g_object_set_data(G_OBJECT(assistant), "options", content);
 
     /* Intégration */
 
+    gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 1);
+
     gtk_assistant_append_page(assistant, alignment);
-    gtk_assistant_set_page_title(assistant, alignment, _("Output"));
+    gtk_assistant_set_page_title(assistant, alignment, _("Format"));
     gtk_assistant_set_page_type(assistant, alignment, GTK_ASSISTANT_PAGE_INTRO);
 
     gtk_assistant_set_page_complete(assistant, alignment, TRUE);
 
-    /* Choix par défaut */
+}
 
-    binary = G_LOADED_BINARY(g_object_get_data(G_OBJECT(assistant), "binary"));
-    filename = g_loaded_binary_get_name(binary, true);
 
-    gtk_entry_set_text(GTK_ENTRY(entry), filename);
-    gtk_editable_insert_text(GTK_EDITABLE(entry), ".txt", -1, (gint []) { strlen(filename) });
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : combo     = liste dont la sélection vient de changer.        *
+*                assistant = fenêtre affichée et référencement global.        *
+*                                                                             *
+*  Description : Réagit un changement du format pour l'exportation.           *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
 
-    g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(on_export_filename_changed), assistant);
+static void on_export_format_changed(GtkComboBox *combo, GtkAssistant *assistant)
+{
+    BufferExportType selected;              /* Format attendu              */
+    GtkContainer *content;                  /* Accueil des options         */
+    GtkWidget *old;                         /* Ancien support à remplacer  */
+    GtkWidget *vbox;                        /* Support principal #1        */
+    GtkWidget *hbox;                        /* Support principal #2        */
+    GtkWidget *label;                       /* Etiquette d'indication      */
+    GtkWidget *entry;                       /* Zone de saisie de valeur    */
+    char *filename;                         /* Chemin à venir modifier     */
+    char *dot;                              /* Dernière occurence de point */
+
+    selected = (BufferExportType)gtk_combo_box_get_active(combo);
+
+    content = GTK_CONTAINER(g_object_get_data(G_OBJECT(assistant), "options"));
+
+    old = gtk_bin_get_child(GTK_BIN(content));
+    if (old != NULL)
+    {
+        g_object_ref(G_OBJECT(old));
+        gtk_container_remove(content, old);
+    }
+
+    switch (selected)
+    {
+        case BET_TEXT:
+
+            hbox = GTK_WIDGET(g_object_get_data(G_OBJECT(assistant), "text_options"));
+
+            if (hbox == NULL)
+            {
+                hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
+                g_object_ref(G_OBJECT(hbox));
+                gtk_widget_show(hbox);
+                g_object_set_data(G_OBJECT(assistant), "text_options", hbox);
+
+                label = qck_create_label(NULL, NULL, _("String between columns: "));
+                gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+                entry = qck_create_entry(G_OBJECT(assistant), "text_separator", NULL);
+                g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(forbid_text_empty_entry), assistant);
+                gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
+                gtk_entry_set_text(GTK_ENTRY(entry), "\\t");
+
+            }
+
+            gtk_container_add(content, hbox);
+
+            break;
+
+        case BET_HTML:
+
+            vbox = GTK_WIDGET(g_object_get_data(G_OBJECT(assistant), "html_options"));
+
+            if (vbox == NULL)
+            {
+                vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
+                g_object_ref(G_OBJECT(vbox));
+                gtk_widget_show(vbox);
+                g_object_set_data(G_OBJECT(assistant), "html_options", vbox);
+
+                hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
+                gtk_widget_show(hbox);
+                gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+                label = qck_create_label(NULL, NULL, _("HTML table font name: "));
+                gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+                entry = qck_create_entry(G_OBJECT(assistant), "html_font_name", NULL);
+                g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(forbid_html_empty_entry), assistant);
+                gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
+                gtk_entry_set_text(GTK_ENTRY(entry), "monospace");
+
+                hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
+                gtk_widget_show(hbox);
+                gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+                label = qck_create_label(NULL, NULL, _("HTML table background color: "));
+                gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+                entry = qck_create_entry(G_OBJECT(assistant), "html_bg_color", NULL);
+                g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(forbid_html_empty_entry), assistant);
+                gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
+                gtk_entry_set_text(GTK_ENTRY(entry), "#2c2c2c");
+
+            }
+
+            gtk_container_add(content, vbox);
+
+            break;
+
+        default:
+            break;
+
+    }
+
+    /* Mise à jour de l'extension du fichier de sortie, si possible */
+
+    entry = GTK_WIDGET(g_object_get_data(G_OBJECT(assistant), "filename"));
+
+    if (entry != NULL)
+    {
+        filename = strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+
+        dot = strrchr(filename, '.');
+        if (dot == NULL) goto oefc_no_dot;
+
+        *dot = '\0';
+
+        switch (selected)
+        {
+            case BET_TEXT:
+                filename = stradd(filename, ".txt");
+                break;
+            case BET_HTML:
+                filename = stradd(filename, ".html");
+                break;
+            default:
+                break;
+        }
+
+        gtk_entry_set_text(GTK_ENTRY(entry), filename);
+
+ oefc_no_dot:
+
+        free(filename);
+
+    }
 
 }
 
@@ -343,7 +509,7 @@ static void register_output_panel(GtkAssistant *assistant)
 *  Paramètres  : entry     = zone de texte dont le contenu vient de changer.  *
 *                assistant = fenêtre affichée et référencement global.        *
 *                                                                             *
-*  Description : Réagit un changement du nom de fichier pour l'exportation.   *
+*  Description : Interdit un champ de texte vide pour les options HTML.       *
 *                                                                             *
 *  Retour      : -                                                            *
 *                                                                             *
@@ -351,7 +517,7 @@ static void register_output_panel(GtkAssistant *assistant)
 *                                                                             *
 ******************************************************************************/
 
-static void on_export_filename_changed(GtkEntry *entry, GtkAssistant *assistant)
+static void forbid_text_empty_entry(GtkEntry *entry, GtkAssistant *assistant)
 {
     const gchar *text;                      /* Texte saisi dans la zone    */
     gint num;                               /* Etape courante              */
@@ -369,10 +535,10 @@ static void on_export_filename_changed(GtkEntry *entry, GtkAssistant *assistant)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : button = bouton d'édition de la sélection.                   *
-*                ref    = espace de référencement principal.                  *
+*  Paramètres  : _entry    = zone de texte dont le contenu vient de changer.  *
+*                assistant = fenêtre affichée et référencement global.        *
 *                                                                             *
-*  Description : Sélectionne ou non un nouveau fichier de sortie.             *
+*  Description : Interdit un champ de texte vide pour les options de texte.   *
 *                                                                             *
 *  Retour      : -                                                            *
 *                                                                             *
@@ -380,32 +546,46 @@ static void on_export_filename_changed(GtkEntry *entry, GtkAssistant *assistant)
 *                                                                             *
 ******************************************************************************/
 
-static void on_filename_browsing_clicked(GtkButton *button, GObject *ref)
+static void forbid_html_empty_entry(GtkEntry *_entry, GtkAssistant *assistant)
 {
-    GtkWidget *dialog;                      /* Boîte à afficher            */
-    gchar *filename;                        /* Nom du fichier à intégrer   */
-    GtkEntry *entry;                        /* Zone de saisie à maj.       */
+    bool status;                            /* Etat final à remonter       */
+    GtkEntry *entry;                        /* Zone de texte générique     */
+    const gchar *text;                      /* Texte saisi dans la zone    */
+    gint num;                               /* Etape courante              */
+    GtkWidget *page;                        /* Support de cette étape      */
 
-    dialog = gtk_file_chooser_dialog_new(_("Choose an output filename"), GTK_WINDOW(ref),
-                                         GTK_FILE_CHOOSER_ACTION_SAVE,
-                                         _("_Cancel"), GTK_RESPONSE_CANCEL,
-                                         _("_Save"), GTK_RESPONSE_ACCEPT,
-                                         NULL);
+    status = true;
 
-    entry = GTK_ENTRY(g_object_get_data(ref, "filename"));
-    gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), gtk_entry_get_text(entry));
+    /* Police de caractère */
 
-    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
-    {
-        filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+    entry = GTK_ENTRY(g_object_get_data(G_OBJECT(assistant), "html_font_name"));
+    text = gtk_entry_get_text(entry);
 
-        gtk_entry_set_text(GTK_ENTRY(entry), filename);
+    status &= (strlen(text) > 0);
 
-        g_free(filename);
+    /* Couleur de fond */
+
+    entry = GTK_ENTRY(g_object_get_data(G_OBJECT(assistant), "html_bg_color"));
+
+    if (entry != NULL)
+    {
+        text = gtk_entry_get_text(entry);
+
+        status &= (strlen(text) > 0);
 
     }
 
-    gtk_widget_destroy(dialog);
+    /* Mise à jour graphique */
+
+    num = gtk_assistant_get_current_page(assistant);
+
+    if (num != -1)
+    {
+        page = gtk_assistant_get_nth_page(assistant, num);
+
+        gtk_assistant_set_page_complete(assistant, page, status);
+
+    }
 
 }
 
@@ -445,22 +625,22 @@ static void register_content_panel(GtkAssistant *assistant)
 
     /* Eléments à afficher */
 
-    frame = qck_create_frame(_("<b>Items to display</b>"), &subalign, 0, 0, 12, 0);
+    frame = qck_create_frame(_("<b>Items to display</b>"), &subalign, 0, 12, 12, 0);
     gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
 
     sub_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
     gtk_widget_show(sub_vbox);
     gtk_container_add(GTK_CONTAINER(subalign), sub_vbox);
 
-    checkbutton = qck_create_check_button(G_OBJECT(assistant), "virtual_addr", _("Virtual address"), NULL, NULL);
+    checkbutton = qck_create_check_button(G_OBJECT(assistant), "physical_off", _("Physical offset"), NULL, NULL);
     gtk_box_pack_start(GTK_BOX(sub_vbox), checkbutton, FALSE, FALSE, 0);
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), TRUE);
 
-    checkbutton = qck_create_check_button(G_OBJECT(assistant), "binary_code", _("Binary code"), NULL, NULL);
+    checkbutton = qck_create_check_button(G_OBJECT(assistant), "virtual_addr", _("Virtual address"), NULL, NULL);
     gtk_box_pack_start(GTK_BOX(sub_vbox), checkbutton, FALSE, FALSE, 0);
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), TRUE);
 
-    checkbutton = qck_create_check_button(G_OBJECT(assistant), "assembly_code", _("Assembly code"), NULL, NULL);
+    checkbutton = qck_create_check_button(G_OBJECT(assistant), "binary_code", _("Binary code"), NULL, NULL);
     gtk_box_pack_start(GTK_BOX(sub_vbox), checkbutton, FALSE, FALSE, 0);
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), TRUE);
 
@@ -468,8 +648,151 @@ static void register_content_panel(GtkAssistant *assistant)
 
     gtk_assistant_append_page(assistant, alignment);
     gtk_assistant_set_page_title(assistant, alignment, _("Exported content"));
+    gtk_assistant_set_page_type(assistant, alignment, GTK_ASSISTANT_PAGE_CONTENT);
+
+    gtk_assistant_set_page_complete(assistant, alignment, TRUE);
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/*                          DEFINITION DE LA SORTIE ATTENDUE                          */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : assistant = fenêtre à compléter et référencement global.     *
+*                                                                             *
+*  Description : Ajoute le panneau de choix du type de sortie.                *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void register_output_panel(GtkAssistant *assistant)
+{
+    GtkWidget *alignment;                   /* Disposition sur le support  */
+    GtkWidget *vbox;                        /* Support principal #1        */
+    GtkWidget *label;                       /* Etiquette d'indication      */
+    GtkWidget *hbox;                        /* Support principal #2        */
+    GtkWidget *entry;                       /* Zone de saisie de texte     */
+    GtkWidget *button;                      /* Sélection de fichier        */
+    GLoadedBinary *binary;                  /* Binaire chargé à parcourir  */
+    const char *filename;                   /* Chemin d'accès par défaut   */
+
+    alignment = qck_create_padded_alignment(8, 8, 8, 8);
+
+    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
+    gtk_widget_show(vbox);
+    gtk_container_add(GTK_CONTAINER(alignment), vbox);
+
+    /* Fichier de sortie */
+
+    label = qck_create_label(NULL, NULL, _("File: "));
+    gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
+    gtk_widget_show(hbox);
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+    entry = qck_create_entry(G_OBJECT(assistant), "filename", NULL);
+    gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
+
+    button = qck_create_button(NULL, NULL, "...", G_CALLBACK(on_filename_browsing_clicked), assistant);
+    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+    /* Intégration */
+
+    gtk_assistant_append_page(assistant, alignment);
+    gtk_assistant_set_page_title(assistant, alignment, _("Output"));
     gtk_assistant_set_page_type(assistant, alignment, GTK_ASSISTANT_PAGE_CONFIRM);
 
     gtk_assistant_set_page_complete(assistant, alignment, TRUE);
 
+    /* Choix par défaut */
+
+    binary = G_LOADED_BINARY(g_object_get_data(G_OBJECT(assistant), "binary"));
+    filename = g_loaded_binary_get_name(binary, true);
+
+    gtk_entry_set_text(GTK_ENTRY(entry), filename);
+    gtk_editable_insert_text(GTK_EDITABLE(entry), ".html", -1, (gint []) { strlen(filename) });
+
+    g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(on_export_filename_changed), assistant);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : entry     = zone de texte dont le contenu vient de changer.  *
+*                assistant = fenêtre affichée et référencement global.        *
+*                                                                             *
+*  Description : Réagit un changement du nom de fichier pour l'exportation.   *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_export_filename_changed(GtkEntry *entry, GtkAssistant *assistant)
+{
+    const gchar *text;                      /* Texte saisi dans la zone    */
+    gint num;                               /* Etape courante              */
+    GtkWidget *page;                        /* Support de cette étape      */
+
+    text = gtk_entry_get_text(entry);
+
+    num = gtk_assistant_get_current_page(assistant);
+    page = gtk_assistant_get_nth_page(assistant, num);
+
+    gtk_assistant_set_page_complete(assistant, page, (strlen(text) > 0));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : button = bouton d'édition de la sélection.                   *
+*                ref    = espace de référencement principal.                  *
+*                                                                             *
+*  Description : Sélectionne ou non un nouveau fichier de sortie.             *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_filename_browsing_clicked(GtkButton *button, GObject *ref)
+{
+    GtkWidget *dialog;                      /* Boîte à afficher            */
+    gchar *filename;                        /* Nom du fichier à intégrer   */
+    GtkEntry *entry;                        /* Zone de saisie à maj.       */
+
+    dialog = gtk_file_chooser_dialog_new(_("Choose an output filename"), GTK_WINDOW(ref),
+                                         GTK_FILE_CHOOSER_ACTION_SAVE,
+                                         _("_Cancel"), GTK_RESPONSE_CANCEL,
+                                         _("_Save"), GTK_RESPONSE_ACCEPT,
+                                         NULL);
+
+    entry = GTK_ENTRY(g_object_get_data(ref, "filename"));
+    gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), gtk_entry_get_text(entry));
+
+    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
+    {
+        filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+
+        gtk_entry_set_text(GTK_ENTRY(entry), filename);
+
+        g_free(filename);
+
+    }
+
+    gtk_widget_destroy(dialog);
+
 }
diff --git a/src/glibext/gbufferline.c b/src/glibext/gbufferline.c
index 6b5ab18..54f5fb6 100644
--- a/src/glibext/gbufferline.c
+++ b/src/glibext/gbufferline.c
@@ -51,7 +51,6 @@ typedef struct _buffer_line_column
 } buffer_line_column;
 
 
-
 /* Réinitialise une colonne de ligne. */
 static void reset_column(buffer_line_column *);
 
@@ -71,11 +70,14 @@ static GBufferSegment *get_segment_at(const buffer_line_column *, gint *, GdkScr
 static GBufferSegment *find_near_segment(const buffer_line_column *, GBufferSegment *, GdkScrollDirection, bool *);
 
 /* Met en surbrillance des segments similaires. */
-GSList *highlight_all_same_segments(const buffer_line_column *, GSList *, const GBufferSegment *);
+static GSList *highlight_all_same_segments(const buffer_line_column *, GSList *, const GBufferSegment *);
 
 /* Imprime le contenu d'une colonne de ligne de texte. */
 static void draw_segments_of_column(buffer_line_column *, cairo_t *, gint, gint);
 
+/* Exporte la ligne de texte représentée. */
+static void export_segments_of_column(buffer_line_column *, buffer_export_context *, BufferExportType, int);
+
 
 
 /* ---------------------------- GESTION DE LINE COMPLETE ---------------------------- */
@@ -313,7 +315,7 @@ static GBufferSegment *find_near_segment(const buffer_line_column *column, GBuff
 *                                                                             *
 ******************************************************************************/
 
-GSList *highlight_all_same_segments(const buffer_line_column *column, GSList *list, const GBufferSegment *ref)
+static GSList *highlight_all_same_segments(const buffer_line_column *column, GSList *list, const GBufferSegment *ref)
 {
     size_t i;                               /* Boucle de parcours          */
 
@@ -347,8 +349,8 @@ GSList *highlight_all_same_segments(const buffer_line_column *column, GSList *li
 
 static void draw_segments_of_column(buffer_line_column *column, cairo_t *cairo, gint x_init, gint y)
 {
-    gint x;
-    size_t i;
+    gint x;                                 /* Abscisse d'impression       */
+    size_t i;                               /* Boucle de parcours          */
 
     x = x_init;
 
@@ -358,6 +360,59 @@ static void draw_segments_of_column(buffer_line_column *column, cairo_t *cairo,
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : column = colonne de ligne de texte à manipuler.              *
+*                ctx    = éléments à disposition pour l'exportation.          *
+*                type   = type d'exportation attendue.                        *
+*                span   = fusion de colonnes au sein des cellules ?           *
+*                                                                             *
+*  Description : Exporte la ligne de texte représentée.                       *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void export_segments_of_column(buffer_line_column *column, buffer_export_context *ctx, BufferExportType type, int span)
+{
+    size_t i;                               /* Boucle de parcours          */
+
+    switch (type)
+    {
+        case BET_HTML:
+            switch (span)
+            {
+                case 0:
+                    break;
+                case 1:
+                    dprintf(ctx->fd, "\t\t<TD>");
+                    break;
+                default:
+                    if (span > 0) dprintf(ctx->fd, "\t\t<TD colspan=\"%d\">", span);
+                    break;
+            }
+            break;
+        default:
+            break;
+    }
+
+    for (i = 0; i < column->count; i++)
+        g_buffer_segment_export(column->segments[i], ctx, type);
+
+    switch (type)
+    {
+        case BET_HTML:
+            if (span < 0 || span == 1) dprintf(ctx->fd, "</TD>\n");
+            break;
+        default:
+            break;
+    }
+
+}
+
+
 
 /* ---------------------------------------------------------------------------------- */
 /*                              GESTION DE LINE COMPLETE                              */
@@ -1117,16 +1172,12 @@ void g_buffer_line_draw(GBufferLine *line, cairo_t *cairo, const gint max_widths
 }
 
 
-
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : line    = ligne de texte à manipuler.                        *
-*                fd      = flux ouvert en écriture.                           *
+*                ctx     = éléments à disposition pour l'exportation.         *
 *                type    = type d'exportation attendue.                       *
-*                phys    = indique si les positions doivent être affichées.   *
-*                virt    = indique si les adresses doivent être affichées.    *
-*                code    = indique si le code binaire doit être affiché.      *
-*                content = indique si le gros du contenu doit être affiché.   *
+*                display = règles d'affichage des colonnes modulables.        *
 *                                                                             *
 *  Description : Exporte la ligne de texte représentée.                       *
 *                                                                             *
@@ -1136,19 +1187,70 @@ void g_buffer_line_draw(GBufferLine *line, cairo_t *cairo, const gint max_widths
 *                                                                             *
 ******************************************************************************/
 
-void g_buffer_line_export(GBufferLine *line, int fd, BufferExportType type, bool phys, bool virt, bool code, bool content)
+void g_buffer_line_export(GBufferLine *line, buffer_export_context *ctx, BufferExportType type, const bool *display)
 {
     BufferLineColumn i;                     /* Boucle de parcours          */
+    int col_span;                           /* Fusion de colonnes ?        */
+
+    switch (type)
+    {
+        case BET_HTML:
+            dprintf(ctx->fd, "\t<TR>\n");
+            break;
+        default:
+            break;
+    }
 
     for (i = 0; i < BLC_COUNT; i++)
     {
-        if (i == BLC_PHYSICAL && !phys) continue;
-        if (i == BLC_VIRTUAL && !virt) continue;
-        if (i == BLC_BINARY && !code) continue;
-        if (!(i == BLC_PHYSICAL || i == BLC_VIRTUAL || i == BLC_BINARY) && !content) continue;
+        if (i < BLC_DISPLAY && !display[i]) continue;
+
+        switch (type)
+        {
+            case BET_TEXT:
+                if (i > 0) dprintf(ctx->fd, ctx->sep);
+                break;
+            default:
+                break;
+        }
+
+        /**
+         * Pour la signification des différentes valeurs assignées,
+         * se référer au code de export_segments_of_column().
+         *
+         * En gros :
+         *   - 1  = rien de spécial.
+         *   - >1 = il s'agit de la première cellule fusionnée de la ligne.
+         *   - 0  = fusion déjà faite, on ne peut que rajouter du contenu dedans.
+         *   - <1 = il s'agit de la dernière cellule fusionnée de la ligne.
+         *
+         * On considère qu'une fusion ne peut pas se réaliser sur la dernière
+         * cellule uniquement (ce qui a du sens : c'est inutile).
+         */
 
-        dprintf(fd, "TODO\n");
+        if (i < line->merge_start)
+            col_span = 1;
 
+        else if (i == line->merge_start)
+            col_span = BLC_COUNT - i;
+
+        else
+            col_span = ((i + 1) == BLC_COUNT ? -1 : 0);
+
+        export_segments_of_column(&line->columns[i], ctx, type, col_span);
+
+    }
+
+    switch (type)
+    {
+        case BET_TEXT:
+            dprintf(ctx->fd, "\n");
+            break;
+        case BET_HTML:
+            dprintf(ctx->fd, "</TR>\n");
+            break;
+        default:
+            break;
     }
 
 }
diff --git a/src/glibext/gbufferline.h b/src/glibext/gbufferline.h
index f6ee171..9b35f0b 100644
--- a/src/glibext/gbufferline.h
+++ b/src/glibext/gbufferline.h
@@ -130,7 +130,7 @@ void g_buffer_line_start_merge_at(GBufferLine *, BufferLineColumn);
 void g_buffer_line_draw(GBufferLine *, cairo_t *, const gint [BLC_COUNT], gint, gint, const bool *);
 
 /* Exporte la ligne de texte représentée. */
-void g_buffer_line_export(GBufferLine *, int, BufferExportType, bool, bool, bool, bool);
+void g_buffer_line_export(GBufferLine *, buffer_export_context *, BufferExportType, const bool *);
 
 
 
diff --git a/src/glibext/gbuffersegment.c b/src/glibext/gbuffersegment.c
index 3d9e5a9..1ebb543 100644
--- a/src/glibext/gbuffersegment.c
+++ b/src/glibext/gbuffersegment.c
@@ -200,7 +200,7 @@ static void g_buffer_segment_class_init(GBufferSegmentClass *class)
 
     void define_rendering_pattern(GtkStyleContext *ctx, const char *name, rendering_pattern_t *pattern)
     {
-        GdkRGBA *tmp_color;
+        GdkRGBA *tmp_color;                 /* Description d'une couleur   */
         PangoFontDescription *font_desc;    /* Description d'une police    */
 
         gtk_style_context_save(ctx);
@@ -219,10 +219,10 @@ static void g_buffer_segment_class_init(GBufferSegmentClass *class)
             case PANGO_STYLE_NORMAL:
                 pattern->slant = CAIRO_FONT_SLANT_NORMAL;
                 break;
-            case PANGO_STYLE_OBLIQUE:
+            case PANGO_STYLE_ITALIC:
                 pattern->slant = CAIRO_FONT_SLANT_ITALIC;
                 break;
-            case PANGO_STYLE_ITALIC:
+            case PANGO_STYLE_OBLIQUE:
                 pattern->slant = CAIRO_FONT_SLANT_OBLIQUE;
                 break;
         }
@@ -303,7 +303,7 @@ GBufferSegment *g_buffer_segment_new(RenderingTagType type, const char *text, si
 
     result = g_object_new(G_TYPE_BUFFER_SEGMENT, NULL);
 
-    result->text = strdup(text);
+    result->text = strndup(text, length);
     result->hash = fnv_64a_hash(text);
 
     class = G_BUFFER_SEGMENT_GET_CLASS(result);
@@ -640,3 +640,125 @@ void g_buffer_segment_draw(GBufferSegment *segment, cairo_t *cairo, gint *x, gin
     *x += segment->x_advance;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : ctx  = éléments à disposition pour l'exportation.            *
+*                type = type d'exportation attendue.                          *
+*                                                                             *
+*  Description : Exporte tous les styles utilisés par des segments.           *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_buffer_segment_export_style(buffer_export_context *ctx, BufferExportType type)
+{
+    GBufferSegment *dummy;                  /* Segment servant de sujet    */
+    GBufferSegmentClass *class;             /* Classe des segments         */
+    size_t i;                               /* Boucle de parcours          */
+    const rendering_pattern_t *pattern;     /* Modèle à transcrire         */
+
+    dummy = g_object_new(G_TYPE_BUFFER_SEGMENT, NULL);
+    class = G_BUFFER_SEGMENT_GET_CLASS(dummy);
+
+    for (i = 0; i < RTT_COUNT; i++)
+    {
+        pattern = &class->patterns[i];
+
+        switch (type)
+        {
+            case BET_HTML:
+
+                dprintf(ctx->fd, ".%s {\n", _segment_names[i]);
+
+                if (pattern->foreground.has_color)
+                    dprintf(ctx->fd, "\tcolor: #%02hhx%02hhx%02hhx;\n",
+                            (unsigned char)(pattern->foreground.color.red * 255),
+                            (unsigned char)(pattern->foreground.color.green * 255),
+                            (unsigned char)(pattern->foreground.color.blue * 255));
+
+                switch (pattern->slant)
+                {
+                    case CAIRO_FONT_SLANT_ITALIC:
+                        dprintf(ctx->fd, "\tfont-style: italic;\n");
+                        break;
+                    case CAIRO_FONT_SLANT_OBLIQUE:
+                        dprintf(ctx->fd, "\tfont-style: oblique;\n");
+                        break;
+                    default:
+                        dprintf(ctx->fd, "\tfont-style: normal;\n");
+                        break;
+                }
+
+                switch (pattern->weight)
+                {
+                    case CAIRO_FONT_WEIGHT_BOLD:
+                        dprintf(ctx->fd, "\tfont-weight: bold;\n");
+                        break;
+                    default:
+                        dprintf(ctx->fd, "\tfont-weight: normal;\n");
+                        break;
+                }
+
+                dprintf(ctx->fd, "}\n");
+
+                break;
+
+            default:
+                break;
+
+        }
+
+    }
+
+    g_object_unref(G_OBJECT(dummy));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : segment = fragment de texte à manipuler.                     *
+*                ctx     = éléments à disposition pour l'exportation.         *
+*                type    = type d'exportation attendue.                       *
+*                                                                             *
+*  Description : Exporte le fragment de texte représenté.                     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_buffer_segment_export(const GBufferSegment *segment, buffer_export_context *ctx, BufferExportType type)
+{
+    GBufferSegmentClass *class;             /* Classe des segments         */
+    size_t index;                           /* Indice du modèle de rendu   */
+
+    switch (type)
+    {
+        case BET_HTML:
+            class = G_BUFFER_SEGMENT_GET_CLASS(segment);
+            index = (segment->pattern - class->patterns);
+            dprintf(ctx->fd, "<SPAN class=\"%s\">", _segment_names[index]);
+            break;
+        default:
+            break;
+    }
+
+    dprintf(ctx->fd, "%s", segment->text);
+
+    switch (type)
+    {
+        case BET_HTML:
+            dprintf(ctx->fd, "</SPAN>");
+            break;
+        default:
+            break;
+    }
+
+}
diff --git a/src/glibext/gbuffersegment.h b/src/glibext/gbuffersegment.h
index f5cd4b2..10ae374 100644
--- a/src/glibext/gbuffersegment.h
+++ b/src/glibext/gbuffersegment.h
@@ -91,15 +91,6 @@ typedef enum _SegRenderingStyle
 
 } SegRenderingStyle;
 
-/* Types d'exportation */
-typedef enum _BufferExportType
-{
-    BET_TEXT,
-
-    BET_COUNT
-
-} BufferExportType;
-
 
 /* Fragment de caractères aux propriétés communes (instance) */
 typedef struct _GBufferSegment GBufferSegment;
@@ -136,6 +127,48 @@ void g_buffer_segment_set_style(GBufferSegment *, SegRenderingStyle);
 /* Imprime le fragment de texte représenté. */
 void g_buffer_segment_draw(GBufferSegment *, cairo_t *, gint *, gint);
 
+/* Types d'exportation */
+typedef enum _BufferExportType
+{
+    BET_TEXT,                               /* Exportation en texte brut   */
+    BET_HTML,                               /* Exportation en HTML         */
+
+    BET_COUNT
+
+} BufferExportType;
+
+/* Elements sur lesquels une exportation peut s'appuyer */
+typedef struct _buffer_export_context
+{
+    union
+    {
+        int fd;                             /* Flux ouvert en écriture     */
+
+    };
+
+    union
+    {
+        /* BET_TEXT */
+        const char *sep;                    /* Séparation entre colonnes   */
+
+        /* BET_HTML */
+        struct
+        {
+            const char *font_name;          /* Police d'impression         */
+            const char *bg_color;           /* Fond du tableau HTML        */
+
+        };
+
+    };
+
+} buffer_export_context;
+
+/* Exporte tous les styles utilisés par des segments. */
+void g_buffer_segment_export_style(buffer_export_context *, BufferExportType);
+
+/* Exporte le fragment de texte représenté. */
+void g_buffer_segment_export(const GBufferSegment *, buffer_export_context *, BufferExportType);
+
 
 
 #endif  /* _GLIBEXT_GBUFFERSEGMENT_H */
diff --git a/src/glibext/gcodebuffer.c b/src/glibext/gcodebuffer.c
index eab418e..d13c9d6 100644
--- a/src/glibext/gcodebuffer.c
+++ b/src/glibext/gcodebuffer.c
@@ -1572,6 +1572,78 @@ void g_buffer_view_draw(const GBufferView *view, cairo_t *cr, gint fake_x, gint
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : view    = visualisation à représenter.                       *
+*                ctx     = éléments à disposition pour l'exportation.         *
+*                type    = type d'exportation attendue.                       *
+*                display = règles d'affichage des colonnes modulables.        *
+*                                                                             *
+*  Description : Exporte le contenu du tampon de code désassemblé.            *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_buffer_view_export(const GBufferView *view, buffer_export_context *ctx, BufferExportType type, const bool *display)
+{
+    size_t start;                           /* Première ligne visée        */
+    size_t end;                             /* Dernière ligne avant limite */
+    GBufferLine **lines;                    /* Liste des lignes à traiter  */
+    size_t i;                               /* Boucle de parcours          */
+
+    start = g_code_buffer_get_index_from_address(view->buffer, view->start, true);
+    end = g_code_buffer_get_index_from_address(view->buffer, view->end, false);
+
+    lines = view->buffer->lines;
+
+    switch (type)
+    {
+        case BET_HTML:
+            dprintf(ctx->fd, "<HTML>\n");
+            dprintf(ctx->fd, "<HEAD>\n");
+            dprintf(ctx->fd, "\t<META http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n");
+            dprintf(ctx->fd, "</HEAD>\n");
+            dprintf(ctx->fd, "<BODY>\n");
+            dprintf(ctx->fd, "<STYLE  type=\"text/css\">\n");
+            dprintf(ctx->fd, "TABLE {\n");
+            dprintf(ctx->fd, "\tbackground-color: %s;\n", ctx->bg_color);
+            dprintf(ctx->fd, "\tborder: 0px;\n");
+            dprintf(ctx->fd, "\tfont-family: %s;\n", ctx->font_name);
+            dprintf(ctx->fd, "}\n");
+            dprintf(ctx->fd, "TD {\n");
+            dprintf(ctx->fd, "\tborder: 0px;\n");
+            dprintf(ctx->fd, "\tpadding-left: 8px;\n");
+            dprintf(ctx->fd, "\tpadding-right: 8px;\n");
+            dprintf(ctx->fd, "}\n");
+            g_buffer_segment_export_style(ctx, type);
+            dprintf(ctx->fd, "</STYLE>\n");
+            dprintf(ctx->fd, "<TABLE>\n");
+            break;
+        default:
+            break;
+    }
+
+    if (view->buffer->used > 0)
+        for (i = start; i <= end; i++)
+            g_buffer_line_export(lines[i], ctx, type, display);
+
+    switch (type)
+    {
+        case BET_HTML:
+            dprintf(ctx->fd, "</TABLE>\n");
+            dprintf(ctx->fd, "</BODY>\n");
+            dprintf(ctx->fd, "</HTML>\n");
+            break;
+        default:
+            break;
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : view = visualisation à consulter.                            *
 *                y    = ordonnée comprise dans la ligne recherchée.           *
 *                idx  = indice de la ligne trouvée ou NULL. [OUT]             *
diff --git a/src/glibext/gcodebuffer.h b/src/glibext/gcodebuffer.h
index ece7c59..62b725c 100644
--- a/src/glibext/gcodebuffer.h
+++ b/src/glibext/gcodebuffer.h
@@ -82,7 +82,7 @@ void g_code_buffer_dec_indentation(GCodeBuffer *);
 typedef bool (* process_line_fc) (GCodeBuffer *, GBufferLine *, void *);
 
 /* Exporte dans un fichier le tampon de code désassemblé. */
-void g_buffer_code_scan(GCodeBuffer *, vmpa_t, vmpa_t, const char *, process_line_fc, void *);
+void g_buffer_code_scan(GCodeBuffer *, vmpa_t, vmpa_t, const char *, process_line_fc, void *) __attribute__ ((deprecated));
 
 
 
@@ -143,6 +143,9 @@ void g_buffer_view_highlight_segments(GBufferView *, gint, gint);
 /* Imprime la visualisation du tampon de code désassemblé. */
 void g_buffer_view_draw(const GBufferView *, cairo_t *, gint, gint, const cairo_rectangle_int_t *, const bool *);
 
+/* Exporte le contenu du tampon de code désassemblé. */
+void g_buffer_view_export(const GBufferView *, buffer_export_context *, BufferExportType, const bool *);
+
 /* Fournit la ligne présente à une ordonnée donnée. */
 GBufferLine *g_buffer_view_find_line_at(GBufferView *, gint, size_t *);
 
-- 
cgit v0.11.2-87-g4458