Add text layout functions copied from libgnomeui/gnome-icon-text.[ch] and

* libnautilus-extensions/nautilus-scalable-font.c:
	(text_layout_free_row), (nautilus_text_layout_free),
	(nautilus_text_layout_new), (nautilus_text_layout_paint):
	* libnautilus-extensions/nautilus-scalable-font.h:
	Add text layout functions copied from
	libgnomeui/gnome-icon-text.[ch] and modified to work with
	NautilusScalalbleFont and GdkPixbuf instead of GdkFont and
	GdkDrawable.

	* test/test-nautilus-font.c: (main):
	Add text layout test.
This commit is contained in:
Ramiro Estrugo 2000-09-07 08:52:43 +00:00
parent e38dfd4fc3
commit 4c7dc0ab85
6 changed files with 766 additions and 8 deletions

View file

@ -1,3 +1,17 @@
2000-09-07 Ramiro Estrugo <ramiro@eazel.com>
* libnautilus-extensions/nautilus-scalable-font.c:
(text_layout_free_row), (nautilus_text_layout_free),
(nautilus_text_layout_new), (nautilus_text_layout_paint):
* libnautilus-extensions/nautilus-scalable-font.h:
Add text layout functions copied from
libgnomeui/gnome-icon-text.[ch] and modified to work with
NautilusScalalbleFont and GdkPixbuf instead of GdkFont and
GdkDrawable.
* test/test-nautilus-font.c: (main):
Add text layout test.
2000-09-07 Ramiro Estrugo <ramiro@eazel.com>
* libnautilus-extensions/nautilus-icon-factory.c: (embed_text):

View file

@ -1327,6 +1327,308 @@ initialize_global_stuff_if_needed (void)
}
}
/*
* The following text_layout stuff was shamelessly plundered
* from libgnomeui/gnome-icon-text.[ch] by Federico Mena.
*
* It was hacked to use NautilusScalableFont and GdkPixbuf
* instead of GdkFont and GdkDrawable. We want to use the
* same layout algorithm in Nautilus so that both the smooth
* and not smooth text rendering cases have predictably
* similar result.
*
* I also made some minor Nautilus-like style changes. -re
*
*/
static void
text_layout_free_row (gpointer data, gpointer user_data)
{
NautilusTextLayoutRow *row;
if (data) {
row = data;
g_free (row->text);
g_free (row);
}
}
/**
* nautilus_text_layout_free:
* @ti: An icon text info structure.
*
* Frees a &NautilusTextLayout structure. You should call this instead of
* freeing the structure yourself.
*/
void
nautilus_text_layout_free (NautilusTextLayout *text_info)
{
g_list_foreach (text_info->rows, text_layout_free_row, NULL);
g_list_free (text_info->rows);
g_free (text_info);
}
/**
* nautilus_text_layout_new:
* @font: Name of the font that will be used to render the text.
* @text: Text to be formatted.
* @separators: Separators used for word wrapping, can be NULL.
* @max_width: Width in pixels to be used for word wrapping.
* @confine: Whether it is mandatory to wrap at @max_width.
*
* Creates a new &NautilusTextLayout structure by wrapping the specified
* text. If non-NULL, the @separators argument defines a set of characters
* to be used as word delimiters for performing word wrapping. If it is
* NULL, then only spaces will be used as word delimiters.
*
* The @max_width argument is used to specify the width at which word
* wrapping will be performed. If there is a very long word that does not
* fit in a single line, the @confine argument can be used to specify
* whether the word should be unconditionally split to fit or whether
* the maximum width should be increased as necessary.
*
* Return value: A newly-created &NautilusTextLayout structure.
*/
NautilusTextLayout *
nautilus_text_layout_new (const NautilusScalableFont *font,
guint font_size,
const char *text,
const char *separators,
int max_width,
gboolean confine)
{
NautilusTextLayout *text_info;
NautilusTextLayoutRow *row;
const char *row_end;
const char *s, *word_start, *word_end, *old_word_end;
char *sub_text;
int i, w_len, w;
const char *text_iter;
int text_len, separators_len;
g_return_val_if_fail (font != NULL, NULL);
g_return_val_if_fail (font_size > 0, NULL);
g_return_val_if_fail (text != NULL, NULL);
g_return_val_if_fail (nautilus_strlen (text) > 0, NULL);
if (!separators)
separators = " ";
text_len = strlen (text);
separators_len = strlen (separators);
text_info = g_new (NautilusTextLayout, 1);
text_info->rows = NULL;
text_info->font = font;
text_info->font_size = font_size;
text_info->width = 0;
text_info->height = 0;
text_info->baseline_skip = font_size;
word_end = NULL;
text_iter = text;
while (*text_iter) {
for (row_end = text_iter; *row_end != 0 && *row_end != '\n'; row_end++);
/* Accumulate words from this row until they don't fit in the max_width */
s = text_iter;
while (s < row_end) {
word_start = s;
old_word_end = word_end;
for (word_end = word_start; *word_end; word_end++) {
const char *p;
for (p = separators; *p; p++) {
if (*word_end == *p)
goto found;
}
}
found:
if (word_end < row_end)
word_end++;
if (nautilus_scalable_font_text_width (font, font_size, font_size, text_iter, word_end - text_iter) > max_width) {
if (word_start == text_iter) {
if (confine) {
/* We must force-split the word. Look for a proper
* place to do it.
*/
w_len = word_end - word_start;
for (i = 1; i < w_len; i++) {
w = nautilus_scalable_font_text_width (font, font_size, font_size, word_start, i);
if (w > max_width) {
if (i == 1)
/* Shit, not even a single character fits */
max_width = w;
else
break;
}
}
/* Create sub-row with the chars that fit */
sub_text = g_new (char, i);
memcpy (sub_text, word_start, (i - 1) * sizeof (char));
sub_text[i - 1] = 0;
row = g_new (NautilusTextLayoutRow, 1);
row->text = sub_text;
row->text_length = i - 1;
row->width = nautilus_scalable_font_text_width (font, font_size, font_size,
sub_text,
strlen (sub_text));
row->text = g_strdup (sub_text);
if (row->text == NULL)
row->text = g_strdup("");
text_info->rows = g_list_append (text_info->rows, row);
if (row->width > text_info->width)
text_info->width = row->width;
text_info->height += text_info->baseline_skip;
/* Bump the text pointer */
text_iter += i - 1;
s = text_iter;
continue;
} else
max_width = nautilus_scalable_font_text_width (font, font_size, font_size, word_start, word_end - word_start);
continue; /* Retry split */
} else {
word_end = old_word_end; /* Restore to region that does fit */
break; /* Stop the loop because we found something that doesn't fit */
}
}
s = word_end;
}
/* Append row */
if (text_iter == row_end) {
/* We are on a newline, so append an empty row */
text_info->rows = g_list_append (text_info->rows, NULL);
text_info->height += text_info->baseline_skip / 2;
/* Next! */
text_iter = row_end + 1;
} else {
/* Create subrow and append it to the list */
int sub_len;
sub_len = word_end - text_iter;
sub_text = g_new (char, sub_len + 1);
memcpy (sub_text, text_iter, sub_len * sizeof (char));
sub_text[sub_len] = 0;
row = g_new (NautilusTextLayoutRow, 1);
row->text = sub_text;
row->text_length = sub_len;
row->width = nautilus_scalable_font_text_width (font, font_size, font_size, sub_text, sub_len);
row->text = g_strdup (sub_text);
if (row->text == NULL)
row->text = g_strdup("");
text_info->rows = g_list_append (text_info->rows, row);
if (row->width > text_info->width)
text_info->width = row->width;
text_info->height += text_info->baseline_skip;
/* Next! */
text_iter = word_end;
}
}
return text_info;
}
/**
* nautilus_text_layout_paint:
* @ti: An icon text info structure.
* @drawable: Target drawable.
* @gc: GC used to render the string.
* @x: Left coordinate for text.
* @y: Upper coordinate for text.
* @just: Justification for text.
*
* Paints the formatted text in the icon text info structure onto a drawable.
* This is just a sample implementation; applications can choose to use other
* rendering functions.
*/
void
nautilus_text_layout_paint (const NautilusTextLayout *text_info,
GdkPixbuf *destination_pixbuf,
int x,
int y,
GtkJustification just,
guint32 color)
{
GList *item;
const NautilusTextLayoutRow *row;
int xpos;
g_return_if_fail (text_info != NULL);
g_return_if_fail (destination_pixbuf != NULL);
/* y += text_info->font->ascent; */
for (item = text_info->rows; item; item = item->next) {
if (item->data) {
row = item->data;
switch (just) {
case GTK_JUSTIFY_LEFT:
xpos = 0;
break;
case GTK_JUSTIFY_RIGHT:
xpos = text_info->width - row->width;
break;
case GTK_JUSTIFY_CENTER:
xpos = (text_info->width - row->width) / 2;
break;
default:
/* Anyone care to implement GTK_JUSTIFY_FILL? */
g_warning ("Justification type %d not supported. Using left-justification.",
(int) just);
xpos = 0;
}
nautilus_scalable_font_draw_text (text_info->font,
destination_pixbuf,
x + xpos,
y,
NULL,
text_info->font_size,
text_info->font_size,
row->text,
row->text_length,
color,
255);
y += text_info->baseline_skip;
} else
y += text_info->baseline_skip / 2;
}
}
#if !defined (NAUTILUS_OMIT_SELF_CHECK)
void

View file

@ -155,6 +155,51 @@ gboolean nautilus_scalable_font_query_font (c
NautilusStringList **slants,
NautilusStringList **set_widths);
/*
* The following text_layout stuff was shamelessly plundered
* from libgnomeui/gnome-icon-text.[ch] by Federico Mena.
*
* It was hacked to use NautilusScalableFont and GdkPixbuf
* instead of GdkFont and GdkDrawable. We want to use the
* same layout algorithm in Nautilus so that both the smooth
* and not smooth text rendering cases have predictably
* similar result.
*
* I also made some minor Nautilus-like style changes. -re
*/
typedef struct
{
char *text;
int width;
int text_length;
} NautilusTextLayoutRow;
typedef struct
{
GList *rows;
const NautilusScalableFont *font;
guint font_size;
int width;
int height;
int baseline_skip;
} NautilusTextLayout;
NautilusTextLayout *nautilus_text_layout_new (const NautilusScalableFont *font,
guint font_size,
const char *text,
const char *separators,
int max_width,
gboolean confine);
void nautilus_text_layout_paint (const NautilusTextLayout *text_info,
GdkPixbuf *pixbuf,
int x,
int y,
GtkJustification just,
guint32 color);
void nautilus_text_layout_free (NautilusTextLayout *text_info);
END_GNOME_DECLS
#endif /* NAUTILUS_SCALABLE_FONT_H */

View file

@ -1327,6 +1327,308 @@ initialize_global_stuff_if_needed (void)
}
}
/*
* The following text_layout stuff was shamelessly plundered
* from libgnomeui/gnome-icon-text.[ch] by Federico Mena.
*
* It was hacked to use NautilusScalableFont and GdkPixbuf
* instead of GdkFont and GdkDrawable. We want to use the
* same layout algorithm in Nautilus so that both the smooth
* and not smooth text rendering cases have predictably
* similar result.
*
* I also made some minor Nautilus-like style changes. -re
*
*/
static void
text_layout_free_row (gpointer data, gpointer user_data)
{
NautilusTextLayoutRow *row;
if (data) {
row = data;
g_free (row->text);
g_free (row);
}
}
/**
* nautilus_text_layout_free:
* @ti: An icon text info structure.
*
* Frees a &NautilusTextLayout structure. You should call this instead of
* freeing the structure yourself.
*/
void
nautilus_text_layout_free (NautilusTextLayout *text_info)
{
g_list_foreach (text_info->rows, text_layout_free_row, NULL);
g_list_free (text_info->rows);
g_free (text_info);
}
/**
* nautilus_text_layout_new:
* @font: Name of the font that will be used to render the text.
* @text: Text to be formatted.
* @separators: Separators used for word wrapping, can be NULL.
* @max_width: Width in pixels to be used for word wrapping.
* @confine: Whether it is mandatory to wrap at @max_width.
*
* Creates a new &NautilusTextLayout structure by wrapping the specified
* text. If non-NULL, the @separators argument defines a set of characters
* to be used as word delimiters for performing word wrapping. If it is
* NULL, then only spaces will be used as word delimiters.
*
* The @max_width argument is used to specify the width at which word
* wrapping will be performed. If there is a very long word that does not
* fit in a single line, the @confine argument can be used to specify
* whether the word should be unconditionally split to fit or whether
* the maximum width should be increased as necessary.
*
* Return value: A newly-created &NautilusTextLayout structure.
*/
NautilusTextLayout *
nautilus_text_layout_new (const NautilusScalableFont *font,
guint font_size,
const char *text,
const char *separators,
int max_width,
gboolean confine)
{
NautilusTextLayout *text_info;
NautilusTextLayoutRow *row;
const char *row_end;
const char *s, *word_start, *word_end, *old_word_end;
char *sub_text;
int i, w_len, w;
const char *text_iter;
int text_len, separators_len;
g_return_val_if_fail (font != NULL, NULL);
g_return_val_if_fail (font_size > 0, NULL);
g_return_val_if_fail (text != NULL, NULL);
g_return_val_if_fail (nautilus_strlen (text) > 0, NULL);
if (!separators)
separators = " ";
text_len = strlen (text);
separators_len = strlen (separators);
text_info = g_new (NautilusTextLayout, 1);
text_info->rows = NULL;
text_info->font = font;
text_info->font_size = font_size;
text_info->width = 0;
text_info->height = 0;
text_info->baseline_skip = font_size;
word_end = NULL;
text_iter = text;
while (*text_iter) {
for (row_end = text_iter; *row_end != 0 && *row_end != '\n'; row_end++);
/* Accumulate words from this row until they don't fit in the max_width */
s = text_iter;
while (s < row_end) {
word_start = s;
old_word_end = word_end;
for (word_end = word_start; *word_end; word_end++) {
const char *p;
for (p = separators; *p; p++) {
if (*word_end == *p)
goto found;
}
}
found:
if (word_end < row_end)
word_end++;
if (nautilus_scalable_font_text_width (font, font_size, font_size, text_iter, word_end - text_iter) > max_width) {
if (word_start == text_iter) {
if (confine) {
/* We must force-split the word. Look for a proper
* place to do it.
*/
w_len = word_end - word_start;
for (i = 1; i < w_len; i++) {
w = nautilus_scalable_font_text_width (font, font_size, font_size, word_start, i);
if (w > max_width) {
if (i == 1)
/* Shit, not even a single character fits */
max_width = w;
else
break;
}
}
/* Create sub-row with the chars that fit */
sub_text = g_new (char, i);
memcpy (sub_text, word_start, (i - 1) * sizeof (char));
sub_text[i - 1] = 0;
row = g_new (NautilusTextLayoutRow, 1);
row->text = sub_text;
row->text_length = i - 1;
row->width = nautilus_scalable_font_text_width (font, font_size, font_size,
sub_text,
strlen (sub_text));
row->text = g_strdup (sub_text);
if (row->text == NULL)
row->text = g_strdup("");
text_info->rows = g_list_append (text_info->rows, row);
if (row->width > text_info->width)
text_info->width = row->width;
text_info->height += text_info->baseline_skip;
/* Bump the text pointer */
text_iter += i - 1;
s = text_iter;
continue;
} else
max_width = nautilus_scalable_font_text_width (font, font_size, font_size, word_start, word_end - word_start);
continue; /* Retry split */
} else {
word_end = old_word_end; /* Restore to region that does fit */
break; /* Stop the loop because we found something that doesn't fit */
}
}
s = word_end;
}
/* Append row */
if (text_iter == row_end) {
/* We are on a newline, so append an empty row */
text_info->rows = g_list_append (text_info->rows, NULL);
text_info->height += text_info->baseline_skip / 2;
/* Next! */
text_iter = row_end + 1;
} else {
/* Create subrow and append it to the list */
int sub_len;
sub_len = word_end - text_iter;
sub_text = g_new (char, sub_len + 1);
memcpy (sub_text, text_iter, sub_len * sizeof (char));
sub_text[sub_len] = 0;
row = g_new (NautilusTextLayoutRow, 1);
row->text = sub_text;
row->text_length = sub_len;
row->width = nautilus_scalable_font_text_width (font, font_size, font_size, sub_text, sub_len);
row->text = g_strdup (sub_text);
if (row->text == NULL)
row->text = g_strdup("");
text_info->rows = g_list_append (text_info->rows, row);
if (row->width > text_info->width)
text_info->width = row->width;
text_info->height += text_info->baseline_skip;
/* Next! */
text_iter = word_end;
}
}
return text_info;
}
/**
* nautilus_text_layout_paint:
* @ti: An icon text info structure.
* @drawable: Target drawable.
* @gc: GC used to render the string.
* @x: Left coordinate for text.
* @y: Upper coordinate for text.
* @just: Justification for text.
*
* Paints the formatted text in the icon text info structure onto a drawable.
* This is just a sample implementation; applications can choose to use other
* rendering functions.
*/
void
nautilus_text_layout_paint (const NautilusTextLayout *text_info,
GdkPixbuf *destination_pixbuf,
int x,
int y,
GtkJustification just,
guint32 color)
{
GList *item;
const NautilusTextLayoutRow *row;
int xpos;
g_return_if_fail (text_info != NULL);
g_return_if_fail (destination_pixbuf != NULL);
/* y += text_info->font->ascent; */
for (item = text_info->rows; item; item = item->next) {
if (item->data) {
row = item->data;
switch (just) {
case GTK_JUSTIFY_LEFT:
xpos = 0;
break;
case GTK_JUSTIFY_RIGHT:
xpos = text_info->width - row->width;
break;
case GTK_JUSTIFY_CENTER:
xpos = (text_info->width - row->width) / 2;
break;
default:
/* Anyone care to implement GTK_JUSTIFY_FILL? */
g_warning ("Justification type %d not supported. Using left-justification.",
(int) just);
xpos = 0;
}
nautilus_scalable_font_draw_text (text_info->font,
destination_pixbuf,
x + xpos,
y,
NULL,
text_info->font_size,
text_info->font_size,
row->text,
row->text_length,
color,
255);
y += text_info->baseline_skip;
} else
y += text_info->baseline_skip / 2;
}
}
#if !defined (NAUTILUS_OMIT_SELF_CHECK)
void

View file

@ -155,6 +155,51 @@ gboolean nautilus_scalable_font_query_font (c
NautilusStringList **slants,
NautilusStringList **set_widths);
/*
* The following text_layout stuff was shamelessly plundered
* from libgnomeui/gnome-icon-text.[ch] by Federico Mena.
*
* It was hacked to use NautilusScalableFont and GdkPixbuf
* instead of GdkFont and GdkDrawable. We want to use the
* same layout algorithm in Nautilus so that both the smooth
* and not smooth text rendering cases have predictably
* similar result.
*
* I also made some minor Nautilus-like style changes. -re
*/
typedef struct
{
char *text;
int width;
int text_length;
} NautilusTextLayoutRow;
typedef struct
{
GList *rows;
const NautilusScalableFont *font;
guint font_size;
int width;
int height;
int baseline_skip;
} NautilusTextLayout;
NautilusTextLayout *nautilus_text_layout_new (const NautilusScalableFont *font,
guint font_size,
const char *text,
const char *separators,
int max_width,
gboolean confine);
void nautilus_text_layout_paint (const NautilusTextLayout *text_info,
GdkPixbuf *pixbuf,
int x,
int y,
GtkJustification just,
guint32 color);
void nautilus_text_layout_free (NautilusTextLayout *text_info);
END_GNOME_DECLS
#endif /* NAUTILUS_SCALABLE_FONT_H */

View file

@ -256,6 +256,64 @@ main (int argc, char* argv[])
GREEN,
255);
{
NautilusTextLayout *text_info;
const guint max_text_width = 100;
const char *separators = " -_,;.?/&";
const char *text = "This is a long piece of text!-This is the second piece-Now we have the third piece-And finally the fourth piece";
const guint font_size = 14;
ArtIRect layout_area;
text_info = nautilus_text_layout_new (font,
font_size,
text,
separators,
max_text_width,
TRUE);
g_assert (text_info != NULL);
layout_area.x0 = 20;
layout_area.y0 = 550;
layout_area.x1 = layout_area.x0 + max_text_width;
layout_area.y1 = layout_area.y0 + 130;
draw_rectangle_around (pixbuf, &layout_area, RED);
nautilus_text_layout_paint (text_info,
pixbuf,
layout_area.x0,
layout_area.y0,
GTK_JUSTIFY_LEFT,
BLACK);
layout_area.x0 += (max_text_width + 20);
layout_area.x1 += (max_text_width + 20);
draw_rectangle_around (pixbuf, &layout_area, RED);
nautilus_text_layout_paint (text_info,
pixbuf,
layout_area.x0,
layout_area.y0,
GTK_JUSTIFY_CENTER,
BLACK);
layout_area.x0 += (max_text_width + 20);
layout_area.x1 += (max_text_width + 20);
draw_rectangle_around (pixbuf, &layout_area, RED);
nautilus_text_layout_paint (text_info,
pixbuf,
layout_area.x0,
layout_area.y0,
GTK_JUSTIFY_RIGHT,
BLACK);
nautilus_text_layout_free (text_info);
}
nautilus_gdk_pixbuf_save_to_file (pixbuf, "font_test.png");
g_print ("saving test png file to font_test.png\n");
@ -264,11 +322,3 @@ main (int argc, char* argv[])
return 0;
}
#if 0
icon_text_info = nautilus_icon_layout_text (details->smooth_font,
details->smooth_font_size,
text_piece, _(" -_,;.?/&"),
max_text_width,
TRUE);
#endif