/* Chrysalide - Outil d'analyse de fichiers binaires
* linesegment.c - concentration d'un fragment de caractères aux propriétés communes
*
* Copyright (C) 2010-2014 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* Chrysalide is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Chrysalide is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar. If not, see .
*/
#include "linesegment.h"
#include
#include
#include
#include
#include
#include
#include "../common/extstr.h"
#include "../common/fnv1a.h"
#include "../gtkext/support.h"
/* ------------------------ NATURE POUR UN FRAGMENT DE TEXTE ------------------------ */
/* Nom des éléments CSS */
#define SEGMENT_NAME(s) "segment-" s
static const char *_segment_names[RTT_COUNT] = {
[RTT_NONE] = SEGMENT_NAME("none"),
[RTT_RAW] = SEGMENT_NAME("raw"),
[RTT_RAW_NULL] = SEGMENT_NAME("raw-null"),
[RTT_PRINTABLE] = SEGMENT_NAME("printable"),
[RTT_NOT_PRINTABLE] = SEGMENT_NAME("not-printable"),
[RTT_COMMENT] = SEGMENT_NAME("comment"),
[RTT_INDICATION] = SEGMENT_NAME("indication"),
[RTT_PHYS_ADDR_PAD] = SEGMENT_NAME("phys-addr-padding"),
[RTT_PHYS_ADDR] = SEGMENT_NAME("phys-addr"),
[RTT_VIRT_ADDR_PAD] = SEGMENT_NAME("virt-addr-padding"),
[RTT_VIRT_ADDR] = SEGMENT_NAME("virt-addr"),
[RTT_RAW_CODE] = SEGMENT_NAME("raw-code"),
[RTT_RAW_CODE_NULL] = SEGMENT_NAME("raw-code-null"),
[RTT_LABEL] = SEGMENT_NAME("label"),
[RTT_INSTRUCTION] = SEGMENT_NAME("instruction"),
[RTT_IMMEDIATE] = SEGMENT_NAME("immediate"),
[RTT_REGISTER] = SEGMENT_NAME("register"),
[RTT_PUNCT] = SEGMENT_NAME("punct"),
[RTT_HOOK] = SEGMENT_NAME("hooks"),
[RTT_SIGNS] = SEGMENT_NAME("signs"),
[RTT_LTGT] = SEGMENT_NAME("ltgt"),
[RTT_SECTION] = SEGMENT_NAME("section"),
[RTT_SEGMENT] = SEGMENT_NAME("segment"),
[RTT_STRING] = SEGMENT_NAME("string"),
[RTT_VAR_NAME] = SEGMENT_NAME("var-name"),
[RTT_KEY_WORD] = SEGMENT_NAME("keyword"),
[RTT_ERROR] = SEGMENT_NAME("error"),
};
/* Compléments à Cairo */
#define CAIRO_FONT_SLANT_COUNT 3
#define CAIRO_FONT_WEIGHT_COUNT 2
#define CAIRO_FONTS_COUNT (CAIRO_FONT_SLANT_COUNT * CAIRO_FONT_WEIGHT_COUNT)
#define CAIRO_FONT_INDEX(s, w) ((s) + (w) * CAIRO_FONT_WEIGHT_COUNT)
/* Propriétés de rendu */
typedef struct _rendering_color_t
{
GdkRGBA color; /* Couleur de rendu */
bool has_color; /* Définition en place ? */
} rendering_color_t;
typedef struct _rendering_pattern_t
{
rendering_color_t foreground; /* Couleur d'impression */
rendering_color_t inverted; /* Couleur inversée pour sél. */
cairo_font_slant_t slant; /* Style d'impression */
cairo_font_weight_t weight; /* Poids de la police */
} rendering_pattern_t;
typedef struct _segment_rendering
{
rendering_color_t selection_bg; /* Fond d'impression */
cairo_t *font_ctxts[CAIRO_FONTS_COUNT]; /* Contextes de police */
double x_advances[CAIRO_FONTS_COUNT]; /* Largeurs par caractère */
rendering_pattern_t patterns[RTT_COUNT];/* Modèles d'impression */
} segment_rendering;
/* Configuration globale des rendus */
static segment_rendering _seg_params;
/* ----------------------- ISOLATION DE CONTENUS PARTAGEABLES ----------------------- */
/* Fragment de caractères aux propriétés potentiellement partagées */
struct _line_segment
{
unsigned int ref_count; /* Compteur de références */
rendering_pattern_t *pattern; /* Propriétés du rendu */
fnv64_t hash; /* Empreinte pour comparaisons */
char text[0]; /* Texte brut conservé */
};
/* Conservation de toutes les créations partagées */
static GHashTable *_segcnt_htable;
/* Fournit l'empreinte d'un contenu pour segments. */
static guint get_line_segment_hash(const line_segment *);
/* Détermine si deux contenus pour segments sont identiques. */
static bool is_line_segment_equal(const line_segment *, const line_segment *);
/* Détermine si deux contenus pour segments sont identiques. */
static line_segment *get_shared_segment_content(const line_segment *);
/* Abandonne un contenu pour segments. */
static void release_shared_segment_content(line_segment *);
/* -------------------- GESTION OPTIMALE D'UNE LISTE DE CONTENUS -------------------- */
/* Liste identifiant un ensemble de segments */
struct _segcnt_list
{
fnv64_t *hashes; /* Empreinte pour comparaisons */
size_t count; /* Nommbre de ces empreintes */
};
/* Indique si le contenu d'un segment est notable ou non. */
bool selection_list_has_segment_content(const segcnt_list *, const line_segment *);
/* ---------------------------------------------------------------------------------- */
/* NATURE POUR UN FRAGMENT DE TEXTE */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : - *
* *
* Description : Procède à l'initialisation des paramètres de rendu de texte. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
bool load_segment_rendering_parameters(void)
{
GtkStyleContext *context; /* Contexte pour les styles */
GtkWidgetPath *path; /* Chemin d'accès aux thèmes */
gchar *filename; /* Accès à une image 1x1 */
cairo_font_slant_t s; /* Boucle de parcours #1 */
cairo_font_weight_t w; /* Boucle de parcours #2 */
cairo_t **cr; /* Contexte à créer */
cairo_surface_t *surface; /* Surface pour dessin Cairo */
cairo_text_extents_t extents; /* Couverture des caractères */
RenderingTagType i; /* Boucle de parcours */
/* Création d'un contexte d'accès */
path = gtk_widget_path_new();
gtk_widget_path_append_type(path, G_TYPE_OBJECT);
context = gtk_style_context_new();
gtk_style_context_set_path(context, path);
gtk_style_context_set_screen(context, gdk_screen_get_default());
/* Contextes pour les mesures initiales */
filename = find_pixmap_file("nil.png");
if (filename == NULL) abort();
for (s = CAIRO_FONT_SLANT_NORMAL; s < CAIRO_FONT_SLANT_COUNT; s++)
for (w = CAIRO_FONT_WEIGHT_NORMAL; w < CAIRO_FONT_WEIGHT_COUNT; w++)
{
cr = &_seg_params.font_ctxts[CAIRO_FONT_INDEX(s, w)];
surface = cairo_image_surface_create_from_png(filename);
*cr = cairo_create(surface);
cairo_surface_destroy(surface);
cairo_select_font_face(*cr, "mono", s, w);
cairo_set_font_size(*cr, 13);
cairo_text_extents(*cr, "A", &extents);
_seg_params.x_advances[CAIRO_FONT_INDEX(s, w)] = extents.x_advance;
}
g_free(filename);
/* Fond d'impression */
_seg_params.selection_bg.has_color = true;
_seg_params.selection_bg.color.red = 0.5;
_seg_params.selection_bg.color.green = 0.5;
_seg_params.selection_bg.color.blue = 0.5;
_seg_params.selection_bg.color.alpha = 1.0;
/* Chargement des définitions utiles */
void define_rendering_pattern(GtkStyleContext *ctx, const char *name, rendering_pattern_t *pattern)
{
GdkRGBA *tmp_color; /* Description d'une couleur */
PangoFontDescription *font_desc; /* Description d'une police */
gtk_style_context_save(ctx);
gtk_style_context_add_class(context, name);
gtk_style_context_get(ctx, GTK_STATE_NORMAL, GTK_STYLE_PROPERTY_COLOR, &tmp_color, NULL);
pattern->foreground.has_color = true;
pattern->foreground.color = *tmp_color;
pattern->inverted.has_color = true;
pattern->inverted.color.red = 1.0 - tmp_color->red;
pattern->inverted.color.green = 1.0 - tmp_color->green;
pattern->inverted.color.blue = 1.0 - tmp_color->blue;
pattern->inverted.color.alpha = tmp_color->alpha;
gdk_rgba_free(tmp_color);
gtk_style_context_get(context, GTK_STATE_FLAG_NORMAL, GTK_STYLE_PROPERTY_FONT, &font_desc, NULL);
switch (pango_font_description_get_style(font_desc))
{
case PANGO_STYLE_NORMAL:
pattern->slant = CAIRO_FONT_SLANT_NORMAL;
break;
case PANGO_STYLE_ITALIC:
pattern->slant = CAIRO_FONT_SLANT_ITALIC;
break;
case PANGO_STYLE_OBLIQUE:
pattern->slant = CAIRO_FONT_SLANT_OBLIQUE;
break;
}
switch (pango_font_description_get_weight(font_desc))
{
case PANGO_WEIGHT_THIN:
case PANGO_WEIGHT_ULTRALIGHT:
case PANGO_WEIGHT_LIGHT:
case PANGO_WEIGHT_SEMILIGHT:
case PANGO_WEIGHT_BOOK:
case PANGO_WEIGHT_NORMAL:
case PANGO_WEIGHT_MEDIUM:
pattern->weight = CAIRO_FONT_WEIGHT_NORMAL;
break;
case PANGO_WEIGHT_SEMIBOLD:
case PANGO_WEIGHT_BOLD:
case PANGO_WEIGHT_ULTRABOLD:
case PANGO_WEIGHT_HEAVY:
case PANGO_WEIGHT_ULTRAHEAVY:
pattern->weight = CAIRO_FONT_WEIGHT_BOLD;
break;
}
pango_font_description_free(font_desc);
gtk_style_context_restore(context);
}
for (i = 0; i < RTT_COUNT; i++)
define_rendering_pattern(context, _segment_names[i], &_seg_params.patterns[i]);
/* Nettoyages finaux... */
gtk_widget_path_free(path);
g_object_unref(context);
return true;
}
/* ---------------------------------------------------------------------------------- */
/* ISOLATION DE CONTENUS PARTAGEABLES */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : - *
* *
* Description : Initialise la table mémorisant les contenus pour segments. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool init_segment_content_hash_table(void)
{
_segcnt_htable = g_hash_table_new_full((GHashFunc)get_line_segment_hash,
(GEqualFunc)is_line_segment_equal,
free, NULL);
return (_segcnt_htable != NULL);
}
/******************************************************************************
* *
* Paramètres : - *
* *
* Description : Organise la sortie de la table des contenus pour segments. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void exit_segment_content_hash_table(void)
{
assert(g_hash_table_size(_segcnt_htable) == 0);
g_hash_table_unref(_segcnt_htable);
}
/******************************************************************************
* *
* Paramètres : content = contenu pour segment à consulter. *
* *
* Description : Fournit l'empreinte d'un contenu pour segments. *
* *
* Retour : Empreinte de lu contenu représenté. *
* *
* Remarques : - *
* *
******************************************************************************/
static guint get_line_segment_hash(const line_segment *content)
{
return content->hash;
}
/******************************************************************************
* *
* Paramètres : content = premier contenu pour segment à analyser. *
* other = second contenu pour segment à analyser. *
* *
* Description : Détermine si deux contenus pour segments sont identiques. *
* *
* Retour : Bilan de la comparaison. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool is_line_segment_equal(const line_segment *content, const line_segment *other)
{
bool result; /* Résultat à retourner */
result = (content->pattern == other->pattern);
result &= (cmp_fnv_64a(content->hash, other->hash) == 0);
result &= (strcmp(content->text, other->text) == 0);
return result;
}
/******************************************************************************
* *
* Paramètres : content = premier contenu pour segment à analyser. *
* other = second contenu pour segment à analyser. *
* *
* Description : Détermine si deux contenus pour segments sont identiques. *
* *
* Retour : Bilan de la comparaison. *
* *
* Remarques : - *
* *
******************************************************************************/
static line_segment *get_shared_segment_content(const line_segment *content)
{
line_segment *result; /* Contenu partagé à renvoyer */
gboolean found; /* Le contenu existe déjà ? */
size_t allocated; /* Besoin complet en mémoire */
#ifndef NDEBUG
gboolean created; /* Validation de mise en place */
#endif
/**
* On considère qu'il n'y a pas besoin de mutex ici, puisque toutes les
* opérations se réalisent à priori dans le seul thread principal pour l'affichage.
*/
found = g_hash_table_lookup_extended(_segcnt_htable, content, (gpointer *)&result, NULL);
if (!found)
{
allocated = sizeof(line_segment) + strlen(content->text) + 1;
result = (line_segment *)malloc(allocated);
memcpy(result, content, allocated);
result->ref_count = 1;
#ifndef NDEBUG
created = g_hash_table_insert(_segcnt_htable, result, result);
assert(created);
#else
g_hash_table_insert(_segcnt_htable, result, result);
#endif
}
else
{
assert(result->ref_count < UINT_MAX);
result->ref_count++;
}
return result;
}
/******************************************************************************
* *
* Paramètres : content = contenu pour segments à délaisser. *
* *
* Description : Abandonne un contenu pour segments. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void release_shared_segment_content(line_segment *content)
{
#ifndef NDEBUG
gboolean deleted; /* Validation de suppression */
#endif
/**
* On considère qu'il n'y a pas besoin de mutex ici, puisque toutes les
* opérations se réalisent à priori dans le seul thread principal pour l'affichage.
*/
if (--content->ref_count == 0)
{
#ifndef NDEBUG
deleted = g_hash_table_remove(_segcnt_htable, content);
assert(deleted);
#else
g_hash_table_remove(_segcnt_htable, content);
#endif
}
}
/* ---------------------------------------------------------------------------------- */
/* NATURE DE BASE POUR UN FRAGMENT DE TEXTE */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : type = propriétés de la zone de texte. *
* text = chaîne de caractères à traiter. *
* length = quantité de ces caractères. *
* *
* Description : Crée un nouveau fragment de texte avec des propriétés. *
* *
* Retour : Elément créé ou recyclé. *
* *
* Remarques : - *
* *
******************************************************************************/
line_segment *get_new_line_segment(RenderingTagType type, const char *text, size_t length)
{
line_segment *result; /* Elément à retourner */
char atmp[sizeof(line_segment) + 128]; /* Allocation static facile */
line_segment *content; /* Contenu à mettre en place ? */
assert(length > 0);
if (length < (sizeof(atmp) - sizeof(line_segment)))
content = (line_segment *)atmp;
else
content = (line_segment *)malloc(sizeof(line_segment) + length + 1);
content->pattern = &_seg_params.patterns[type];
content->hash = fnv_64a_hash(text);
memcpy(content->text, text, length);
content->text[length] = '\0';
result = get_shared_segment_content(content);
if (content != (line_segment *)atmp)
free(content);
return result;
}
/******************************************************************************
* *
* Paramètres : segment = fragment de texte à libérer de la mémoire. *
* *
* Description : Retire une utilisation à un fragment de texte. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void release_line_segment(line_segment *segment)
{
release_shared_segment_content(segment);
}
/******************************************************************************
* *
* Paramètres : segment = fragment de texte à consulter. *
* *
* Description : Indique le type de rendu associé à un segment de ligne. *
* *
* Retour : Identifiant de type de rendu. *
* *
* Remarques : - *
* *
******************************************************************************/
RenderingTagType get_line_segment_type(const line_segment *segment)
{
RenderingTagType result; /* Résultat à renvoyer */
result = (RenderingTagType)(segment->pattern - _seg_params.patterns);
return result;
}
/******************************************************************************
* *
* Paramètres : segment = fragment de texte à consulter. *
* markup = indique si le texte doit être décoré ou non. *
* *
* Description : Fournit le texte brut conservé dans le segment. *
* *
* Retour : Texte conservé en interne. *
* *
* Remarques : - *
* *
******************************************************************************/
char *get_line_segment_text(const line_segment *segment, bool markup)
{
char *result; /* Description à renvoyer */
char color[7]; /* Couleur hexadécimale */
char *valid;
/* Résolution du cas simple */
if (!markup)
return strdup(segment->text);
result = strdup("pattern->foreground.color.red * 255),
(unsigned char)(segment->pattern->foreground.color.green * 255),
(unsigned char)(segment->pattern->foreground.color.blue * 255));
result = stradd(result, color);
result = stradd(result, "\"");
/* Style */
result = stradd(result, "style=\"");
switch (segment->pattern->slant)
{
case CAIRO_FONT_SLANT_NORMAL:
result = stradd(result, "normal");
break;
case CAIRO_FONT_SLANT_ITALIC:
result = stradd(result, "italic");
break;
case CAIRO_FONT_SLANT_OBLIQUE:
result = stradd(result, "oblique");
break;
}
result = stradd(result, "\"");
/* Epaisseur */
result = stradd(result, "weight=\"");
switch (segment->pattern->weight)
{
case CAIRO_FONT_WEIGHT_NORMAL:
result = stradd(result, "normal");
break;
case CAIRO_FONT_WEIGHT_BOLD:
result = stradd(result, "bold");
break;
}
result = stradd(result, "\"");
/* Conclusion */
result = stradd(result, ">");
valid = strdup(segment->text);
valid = strrpl(valid, "<", "<");
valid = strrpl(valid, "&", "&");
result = stradd(result, valid);
free(valid);
result = stradd(result, "");
return result;
}
/******************************************************************************
* *
* Paramètres : segment = fragment de texte à consulter. *
* *
* Description : Fournit la quantité de pixels requise pour l'impression. *
* *
* Retour : Largeur requise par la colonne, en pixel. *
* *
* Remarques : - *
* *
******************************************************************************/
gint get_line_segment_width(const line_segment *segment)
{
gint result; /* Largeur à retourner */
cairo_font_slant_t slant; /* Style d'impression */
cairo_font_weight_t weight; /* Poids de la police */
size_t length; /* Taille du texte représenté */
slant = segment->pattern->slant;
weight = segment->pattern->weight;
length = strlen(segment->text);
if (length == 1 && segment->text[0] == '\t')
length = 2;
result = _seg_params.x_advances[CAIRO_FONT_INDEX(slant, weight)] * length;
return result;
}
/******************************************************************************
* *
* Paramètres : segment = fragment de texte à consulter. *
* x = position horizontale au niveau du segment. *
* *
* Description : Fournit la position idéale pour un marqueur. *
* *
* Retour : Position dans le segment donné. *
* *
* Remarques : - *
* *
******************************************************************************/
gint get_caret_position_from_line_segment(const line_segment *segment, gint x)
{
gint result; /* Position à retourner */
gint width; /* Largeur du segment */
gint char_width; /* Largeur de police fixe */
width = get_line_segment_width(segment);
if (x <= 0)
result = 0;
else if (x >= width)
result = width;
else
{
char_width = width / strlen(segment->text);
result = (x / char_width) * char_width;
if ((x % char_width) > (char_width / 2))
result += char_width;
}
return result;
}
/******************************************************************************
* *
* Paramètres : segment = fragment de texte à manipuler. *
* x = position du curseur à faire évoluer. [OUT] *
* ctrl = indique la demande d'un parcours rapide. *
* dir = direction du parcours. *
* *
* Description : Déplace le curseur au sein d'un segment de tampon. *
* *
* Retour : true si un déplacement a été effectué, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool move_caret_on_line_segment(const line_segment *segment, gint *x, bool ctrl, GdkScrollDirection dir)
{
bool result; /* Bilan d'opération à renvoyer*/
gint width; /* Largeur du segment */
gint char_width; /* Largeur de police fixe */
result = false;
width = get_line_segment_width(segment);
char_width = width / strlen(segment->text);
if (dir == GDK_SCROLL_LEFT)
{
if (*x > width) *x = width + char_width;
if (*x == 0) goto gbsmc_done;
if (ctrl) *x = 0;
else *x = MAX(0, *x - char_width);
result = true;
}
else if (dir == GDK_SCROLL_RIGHT)
{
if (*x == width) goto gbsmc_done;
if (ctrl) *x = width;
else *x = MIN(width, *x + char_width);
result = true;
}
gbsmc_done:
return result;
}
/******************************************************************************
* *
* Paramètres : segment = fragment de texte à manipuler. *
* cr = contexte graphique à utiliser pour les pinceaux. *
* x = abscisse du point d'impression (à maj). [OUT] *
* y = ordonnée du point d'impression. *
* list = liste de contenus à mettre en évidence. *
* *
* Description : Imprime le fragment de texte représenté. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void draw_line_segment(const line_segment *segment, cairo_t *cr, gint *x, gint y, const segcnt_list *list)
{
bool selected; /* Marquer une sélection ? */
gint width; /* Largeur du segment */
cairo_operator_t old; /* Sauvegarde avant changement */
const rendering_color_t *used_fg; /* Couleur d'impression utile */
if (segment->text[0] == '\t' && segment->text[1] == '\0')
goto small_sep;
selected = selection_list_has_segment_content(list, segment);
width = get_line_segment_width(segment);
/* Fond du texte */
if (selected)
{
cairo_set_source_rgba(cr,
_seg_params.selection_bg.color.red,
_seg_params.selection_bg.color.green,
_seg_params.selection_bg.color.blue,
_seg_params.selection_bg.color.alpha);
cairo_rectangle(cr, *x, y, width, 17);
old = cairo_get_operator(cr);
cairo_set_operator(cr, CAIRO_OPERATOR_DIFFERENCE);
cairo_fill(cr);
cairo_set_operator(cr, old);
}
/* Couleur d'impression */
if (selected)
used_fg = &segment->pattern->inverted;
else
used_fg = &segment->pattern->foreground;
if (used_fg->has_color)
cairo_set_source_rgba(cr,
used_fg->color.red,
used_fg->color.green,
used_fg->color.blue,
used_fg->color.alpha);
else
cairo_set_source_rgb(cr, 0, 0, 0);
/* Impression du texte */
cairo_select_font_face(cr, "mono", segment->pattern->slant, segment->pattern->weight);
cairo_set_font_size(cr, 13);
cairo_move_to(cr, *x, y + 17 - 3); /* 3 = font extents.descent */
cairo_show_text(cr, segment->text);
small_sep:
*x += width;
}
/******************************************************************************
* *
* 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 export_line_segment_style(buffer_export_context *ctx, BufferExportType type)
{
size_t i; /* Boucle de parcours */
const rendering_pattern_t *pattern; /* Modèle à transcrire */
for (i = 0; i < RTT_COUNT; i++)
{
pattern = &_seg_params.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;
}
}
}
/******************************************************************************
* *
* 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 export_line_segment(const line_segment *segment, buffer_export_context *ctx, BufferExportType type)
{
size_t index; /* Indice du modèle de rendu */
switch (type)
{
case BET_HTML:
index = (segment->pattern - _seg_params.patterns);
dprintf(ctx->fd, "", _segment_names[index]);
break;
default:
break;
}
dprintf(ctx->fd, "%s", segment->text);
switch (type)
{
case BET_HTML:
dprintf(ctx->fd, "");
break;
default:
break;
}
}
/* ---------------------------------------------------------------------------------- */
/* GESTION OPTIMALE D'UNE LISTE DE CONTENUS */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : - *
* *
* Description : Initilise une liste de contenus de segments. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
segcnt_list *init_segment_content_list(void)
{
segcnt_list *result; /* Structure à retourner */
result = (segcnt_list *)calloc(1, sizeof(segcnt_list));
return result;
}
/******************************************************************************
* *
* Paramètres : list = ensemble de références de contenus à traiter. *
* *
* Description : Libère la mémoire occupée par une liste de contenus. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void exit_segment_content_list(segcnt_list *list)
{
reset_segment_content_list(list);
free(list);
}
/******************************************************************************
* *
* Paramètres : list = ensemble de références de contenus à manipuler. *
* *
* Description : Vide, si besoin est, une liste de contenus de segments. *
* *
* Retour : true si des éléments ont été purgés, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool reset_segment_content_list(segcnt_list *list)
{
bool result; /* Bilan d'action à renvoyer */
result = (list->count > 0);
if (list->hashes != NULL)
{
free(list->hashes);
list->hashes = NULL;
}
list->count = 0;
return result;
}
/******************************************************************************
* *
* Paramètres : list = ensemble de références de contenus à manipuler. *
* segment = fragment de texte à conservr. *
* *
* Description : Marque le contenu d'un segment comme remarquable. *
* *
* Retour : true si la liste a été complétée, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool add_segment_content_to_selection_list(segcnt_list *list, const line_segment *segment)
{
bool result; /* Bilan à retourner */
size_t i; /* Boucle de parcours */
static const char white_list[] = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
result = false;
for (i = 0; i < (sizeof(white_list) - 1) && !result; i++)
result = (strchr(segment->text, white_list[i]) != NULL);
if (result)
{
list->hashes = (fnv64_t *)realloc(list->hashes, ++list->count * sizeof(fnv64_t));
list->hashes[list->count - 1] = segment->hash;
}
return result;
}
/******************************************************************************
* *
* Paramètres : list = ensemble de références de contenus à consulter. *
* segment = fragment de texte à comparer. *
* *
* Description : Indique si le contenu d'un segment est notable ou non. *
* *
* Retour : true si le segment a un contenu présent dans la sélection. *
* *
* Remarques : - *
* *
******************************************************************************/
bool selection_list_has_segment_content(const segcnt_list *list, const line_segment *segment)
{
bool result; /* Bilan à retourner */
size_t i; /* Boucle de parcours */
result = false;
for (i = 0; i < list->count && !result; i++)
result = (cmp_fnv_64a(list->hashes[i], segment->hash) == 0);
return result;
}