[Mono-winforms-list] GDI+ and Pango.

Sebastien Pouliot sebastien.pouliot at gmail.com
Wed May 13 07:13:34 EDT 2009


On Tue, 2009-05-12 at 20:06 -0400, Miguel de Icaza wrote:
> Hello,
> 
>     There was a discussion today on IRC about Winforms and Pango, and I
> was wondering if this patch ever got committed, 

No, it's not committed. But like I offered in a later email I don't have
any issue committing this* (well the most up to date patch) into SVN.

> and in general what was the status of using Pango for GDI+

Even with the great work from Jonathan this Pango-enabling does not
cover all the cases already supported by libgdiplus (unless he has a
newer patch with additional features). 

* As such it's not possible at this stage to switch to a pango backend
without loosing features that existing applications depends on. Which
means it will, for the time being, be an option (getting the new
features) that reduce compatibility.

The "big deal" is that the current work (old mine in SVN and Jonathan
new patch) use the high-level pango API which is not complete enough for
some of the options offered by GDI+ (including many supported by
libgdiplus). This means that the code needs to be re-written in the
low-level API before being ready for general consumption.

> > Sorry for the long delay.  I don't have write access to svn, so I'll 
> > need someone to commit it for me, or to get access to do so.
> > 
> > I'm still doing work in this area - right now I'm working with the 
> > issues for complex scripts in the TextBox and similar edit controls.
> > 
> > Thanks,
> > Jonathan
> > 
> > Sebastien Pouliot wrote:
> > > Hello Jonathan,
> > > 
> > > On Thu, 2009-02-12 at 21:47 +0700, Jonathan Anderson wrote:
> > >> Sebastien Pouliot wrote:
> > >>>> There are a couple of issues that I've fixed since the patch I submitted 
> > >>>> as well (fixed a problem with vertical text and another difference with 
> > >>>> how MS GDI+ handles the NoWrap flag).  Should I do another patch with 
> > >>>> everything again, or just a patch on the patched version for those?
> > >>> You can issue a new, complete patch.
> > > 
> > > Sorry for the delay, I wanted to try it (out of curiosity) but did not
> > > have time yet. Anyway this should not block getting the source into SVN.
> > > 
> > > Besides a few spaces/tabs issues (see inline) everything looks fine and
> > > since pango-support is not part of the default build please feel free*
> > > to commit your changes along with the ChangeLog.
> > > 
> > > * as long as it does not touch the "main" source files and it follow the
> > > mono source conventions.
> > > 
> > > Thanks!
> > > Sebastien
> > > 
> > >> Here's the new patch.  Fixes from the previous one:
> > >> * vertical latin text is now drawn with characters in the correct direction
> > >> * NoWrap will still wrap on newline now (MS GDI+ does this)
> > >>
> > >> Jonathan Anderson
> > >> plain text document attachment (pangopatch2.diff)
> > >> Index: src/text-pango-private.h
> > >> ===================================================================
> > >> --- src/text-pango-private.h	(revision 126715)
> > >> +++ src/text-pango-private.h	(working copy)
> > >> @@ -36,17 +36,18 @@
> > >>  #include "graphics-private.h"
> > >>  #include "stringformat-private.h"
> > >>  
> > >> +#define PANGO_MAX (G_MAXINT / PANGO_SCALE)
> > >> +#define MAKE_SAFE_FOR_PANGO(x)	((x) > G_MAXINT/PANGO_SCALE ? G_MAXINT/PANGO_SCALE : ((x) < G_MININT/PANGO_SCALE ? G_MININT/PANGO_SCALE : (x)))
> > >>  
> > >>  #define	GDIP_WINDOWS_ACCELERATOR	'&'
> > >> -#define GDIP_PANGOHACK_ACCELERATOR	((char)1)
> > >>  
> > >>  #define text_DrawString			pango_DrawString
> > >>  #define text_MeasureString		pango_MeasureString
> > >>  #define text_MeasureCharacterRanges	pango_MeasureCharacterRanges
> > >>  
> > >>
> > >> -PangoLayout* gdip_pango_setup_layout (cairo_t *ct, GDIPCONST WCHAR *stringUnicode, int length, GDIPCONST GpFont *font, 
> > >> -	GDIPCONST RectF *rc, RectF *box, GDIPCONST GpStringFormat *format);
> > >> +PangoLayout* gdip_pango_setup_layout (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int length, GDIPCONST GpFont *font, 
> > >> +	GDIPCONST RectF *rc, RectF *box, GDIPCONST GpStringFormat *format, int **charsRemoved);
> > >>  
> > >>  GpStatus pango_DrawString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int length, GDIPCONST GpFont *font, 
> > >>  	GDIPCONST RectF *rc, GDIPCONST GpStringFormat *format, GpBrush *brush) GDIP_INTERNAL;
> > >> Index: src/text-pango.c
> > >> ===================================================================
> > >> --- src/text-pango.c	(revision 126715)
> > >> +++ src/text-pango.c	(working copy)
> > >> @@ -46,45 +46,151 @@
> > >>  	return list;
> > >>  }
> > >>  
> > >> -static void
> > >> -gdip_process_accelerators (gchar *text, int length, PangoAttrList *list)
> > >> +void
> > >> +gdip_set_array_values (int *array, int value, int num)
> > >>  {
> > >>  	int i;
> > >> -	for (i = 0; i < length; i++) {
> > >> -		if (*(text + i) == GDIP_WINDOWS_ACCELERATOR) {
> > >> -			/* don't show the prefix character */
> > >> -			*(text + i) = GDIP_PANGOHACK_ACCELERATOR;
> > >> -			/* if the next character is an accelerator then skip over it (&& == &) */
> > >> -			if ((i < length - 1) && (*(text + i + 1) == GDIP_WINDOWS_ACCELERATOR)) {
> > >> -				i++;
> > >> -			} else if (list) {
> > >> -				/* add an attribute on the next character */
> > >> +	for (i = 0; i < num; i++)
> > >> +		array [i] = value;
> > >> +}
> > >> +
> > >> +static GString *
> > >> +gdip_process_string (gchar *text, int length, int removeAccelerators, int trimSpace, PangoAttrList *list, int **charsRemoved)
> > >> +{
> > >> +	int i, j;
> > >> +	int nonws = 0;
> > >> +	gchar *iter;
> > >> +	gchar *iter2;
> > >> +	gunichar ch;
> > >> +    GString *res = g_string_sized_new (length);
> > > 
> > > ^ space/tab issue
> > > 
> > >> +
> > >> +	/* fast version: just check for final newline and remove */
> > >> +	if (!removeAccelerators && !trimSpace) {
> > >> +		j = length;
> > >> +		if (j > 0 && text [j-1] == '\n') {
> > >> +			j--;
> > >> +			if (j > 0 && text [j-1] == '\r')
> > >> +				j--;
> > >> +		}
> > >> +		g_string_append_len (res, text, j);
> > >> +		if (j == 0 && length > 0) {
> > >> +			g_string_append_c (res, ' ');
> > >> +			j++;
> > >> +		}
> > >> +		if (charsRemoved && *charsRemoved) {
> > >> +			int prevj = (g_utf8_prev_char (res->str + j) - res->str);
> > >> +			gdip_set_array_values (*charsRemoved + prevj, length - j, j - prevj);
> > >> +		}
> > >> +		return res;
> > >> +	}
> > >> +
> > >> +	iter = text;
> > >> +	i = 0;
> > >> +	j = 0;
> > >> +	while (iter - text < length) {
> > >> +		ch = g_utf8_get_char (iter);
> > >> +		if (ch == GDIP_WINDOWS_ACCELERATOR && removeAccelerators && (iter - text < length - 1)) {
> > >> +			nonws = 1;
> > >> +			iter2 = g_utf8_next_char (iter);
> > >> +			i += iter2 - iter;
> > >> +			iter = iter2;
> > >> +			ch = g_utf8_get_char (iter);
> > >> +			/* add an attribute on the next character */
> > >> +			if (list && (iter - text < length) && (ch != GDIP_WINDOWS_ACCELERATOR)) {
> > >>  				PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
> > >> -				attr->start_index = i + 1;
> > >> -				attr->end_index = i + 2;
> > >> +				attr->start_index = j;
> > >> +				attr->end_index = j + g_utf8_next_char (iter) - iter;
> > >>  				pango_attr_list_insert (list, attr);
> > >>  			}
> > >> +		} else if (!g_unichar_isspace (ch)) {
> > >> +			nonws = 1;
> > >> +		} else if (trimSpace && ch != '\r' && ch != '\n') {
> > >> +			/* unless specified we don't consider the trailing spaces, unless there is just one space (#80680) */
> > >> +			for (iter2 = g_utf8_next_char (iter); iter2 - text < length; iter2 = g_utf8_next_char (iter2)) {
> > >> +				ch = g_utf8_get_char (iter2);
> > >> +				if (ch == '\r' || ch == '\n')
> > >> +					break;
> > >> +				if (!g_unichar_isspace (ch)) {
> > >> +					g_string_append_len (res, iter, iter2 - iter);
> > >> +					if (charsRemoved && *charsRemoved)
> > >> +						gdip_set_array_values ((*charsRemoved)+j, i - j, iter2 - iter);
> > >> +					j += iter2 - iter;
> > >> +					break;
> > >> +				}
> > >> +			}
> > >> +			i += iter2 - iter;
> > >> +			iter = iter2;
> > >> +			continue;
> > >> +		} else if ((ch == '\r' && (iter - text == length - 2) && (*g_utf8_next_char (iter) == '\n')) || (ch == '\n' && iter - text == length - 1)) {
> > >> +			/* in any case, ignore a final newline as pango will add an extra line to the measurement while gdi+ does not */
> > >> +			i = length;
> > >> +			break;
> > >>  		}
> > >> +		iter2 = g_utf8_next_char (iter);
> > >> +		g_string_append_len (res, iter, iter2 - iter);
> > >> +		/* save these for string lengths later */
> > >> +		if (charsRemoved && *charsRemoved)
> > >> +			gdip_set_array_values ((*charsRemoved)+j, i - j, iter2 - iter);
> > >> +		j += iter2 - iter;
> > >> +		i += iter2 - iter;
> > >> +		iter = iter2;
> > >>  	}
> > >> +	/* always ensure that at least one space is measured */
> > >> +	if (!nonws && trimSpace) {
> > >> +		g_string_append_c (res, ' ');
> > >> +		j++;
> > >> +	}
> > >> +	if (charsRemoved && *charsRemoved && j > 0) {
> > >> +		int prevj = (g_utf8_prev_char (res->str + j) - res->str);
> > >> +		gdip_set_array_values (*charsRemoved + prevj, i - j, j - prevj);
> > >> +	}
> > >> +	return res;
> > >>  }
> > >>  
> > >>  PangoLayout*
> > >> -gdip_pango_setup_layout (cairo_t *ct, GDIPCONST WCHAR *stringUnicode, int length, GDIPCONST GpFont *font, 
> > >> -	GDIPCONST RectF *rc, RectF *box, GDIPCONST GpStringFormat *format)
> > >> +gdip_pango_setup_layout (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int length, GDIPCONST GpFont *font, 
> > >> +	GDIPCONST RectF *rc, RectF *box, GDIPCONST GpStringFormat *format, int **charsRemoved)
> > >>  {
> > >>  	GpStringFormat *fmt;
> > >>  	PangoLayout *layout;
> > >>  	PangoContext *context;
> > >> -	PangoMatrix matrix = PANGO_MATRIX_INIT;
> > >> -	PangoRectangle logical;
> > >> +	PangoRectangle logical;   /* logical size of text (used for alignment) */
> > >> +	PangoRectangle ink;       /* ink size of text (to pixel boundaries) */
> > >>  	PangoAttrList *list = NULL;
> > >> +    GString *ftext;
> > > 
> > > ^ space/tab issue
> > > 
> > >> +	PangoTabArray *tabs;
> > >> +	PangoLayoutIter *iter;
> > >> +	int i;
> > >> +	int FrameWidth;     /* rc->Width (or rc->Height if vertical) */
> > >> +	int FrameHeight;    /* rc->Height (or rc->Width if vertical) */
> > >> +	int FrameX;         /* rc->X (or rc->Y if vertical) */
> > >> +	int FrameY;         /* rc->Y (or rc->X if vertical) */
> > >> +	int y0;             /* y0,y1,clipNN used for checking line positions vs. clip rectangle */
> > >> +	int y1;
> > >> +	double clipx1;
> > >> +	double clipx2;
> > >> +	double clipy1;
> > >> +	double clipy2;
> > >> +	int trimSpace;      /* whether or not to trim the space */
> > >>  
> > >>  	gchar *text = ucs2_to_utf8 (stringUnicode, length);
> > >>  	if (!text)
> > >>  		return NULL;
> > >> +    length = strlen (text);
> > > 
> > > ^ space/tab issue
> > > 
> > >> -//g_warning ("layout >%s< (%d) [x %g, y %g, w %g, h %g] [font %s, %g points]", text, length, rc->X, rc->Y, rc->Width, rc->Height, font->face, font->emSize);
> > >> +	if (charsRemoved) {
> > >> +		(*charsRemoved) = GdipAlloc (sizeof (int) * length);
> > >> +		if (!*charsRemoved) {
> > >> +			GdipFree (text);
> > >> +			return NULL;
> > >> +		}
> > >> +		memset (*charsRemoved, 0, sizeof (int) * length);
> > >> +	}
> > >>  
> > >> +	/* TODO - Digit substitution */
> > >> +
> > >> +// g_warning ("layout >%s< (%d) [x %g, y %g, w %g, h %g] [font %s, %g points]", text, length, rc->X, rc->Y, rc->Width, FrameHeight, font->face, font->emSize);
> > >> +
> > >>  	/* a NULL format is valid, it means get the generic default values (and free them later) */
> > >>  	if (!format) {
> > >>  		GpStatus status = GdipStringFormatGetGenericDefault ((GpStringFormat **)&fmt);
> > >> @@ -96,36 +202,46 @@
> > >>  		fmt = (GpStringFormat *)format;
> > >>  	}
> > >>  
> > >> -	/* unless specified we don't consider the trailing spaces, unless there is just one space (#80680) */
> > >> -	if ((fmt->formatFlags & StringFormatFlagsMeasureTrailingSpaces) == 0) {
> > >> -		while ((length > 0) && (isspace (*(text + length - 1))))
> > >> -			length--;
> > >> -		if (length == 0)
> > >> -			length = 1;
> > >> -	}
> > >> +	layout = pango_cairo_create_layout (graphics->ct);
> > >>  
> > >> -	layout = pango_cairo_create_layout (ct);
> > >> -
> > >>  	/* context is owned by Pango (i.e. not referenced counted) do not free */
> > >>  	context = pango_layout_get_context (layout);
> > >>  
> > >>  	pango_layout_set_font_description (layout, gdip_get_pango_font_description ((GpFont*) font));
> > >>  
> > >> -	if ((rc->Width <= 0.0) || (fmt->formatFlags & StringFormatFlagsNoWrap)) {
> > >> +	if (fmt->formatFlags & StringFormatFlagsDirectionVertical) {
> > >> +		FrameWidth = MAKE_SAFE_FOR_PANGO (SAFE_FLOAT_TO_UINT32 (rc->Height));
> > >> +		FrameHeight = MAKE_SAFE_FOR_PANGO (SAFE_FLOAT_TO_UINT32 (rc->Width));
> > >> +		FrameX = SAFE_FLOAT_TO_UINT32 (rc->Y);
> > >> +		FrameY = SAFE_FLOAT_TO_UINT32 (rc->X);
> > >> +	} else {
> > >> +		FrameWidth = MAKE_SAFE_FOR_PANGO (SAFE_FLOAT_TO_UINT32 (rc->Width));
> > >> +		FrameHeight = MAKE_SAFE_FOR_PANGO (SAFE_FLOAT_TO_UINT32 (rc->Height));
> > >> +		FrameX = SAFE_FLOAT_TO_UINT32 (rc->X);
> > >> +		FrameY = SAFE_FLOAT_TO_UINT32 (rc->Y);
> > >> +	}
> > >> +	//g_warning("FW: %d\tFH: %d", FrameWidth, FrameHeight);
> > >> +
> > >> +	if ((FrameWidth <= 0) || (fmt->formatFlags & StringFormatFlagsNoWrap)) {
> > >>  		pango_layout_set_width (layout, -1);
> > >> +		//g_warning ("Setting width: %d", -1);
> > >>  	} else {
> > >> -		/* minus one to deal with our AA offset */
> > >> -		int width = rc->Width - 1;
> > >> -		/* TODO incomplete (missing height adjustment) */
> > >> -		if ((fmt->formatFlags & StringFormatFlagsNoFitBlackBox) == 0)
> > >> -			width -= 2;
> > >> -		pango_layout_set_width (layout, width * PANGO_SCALE);
> > >> +		pango_layout_set_width (layout, FrameWidth * PANGO_SCALE);
> > >> +		//g_warning ("Setting width: %d", FrameWidth * PANGO_SCALE);
> > >>  	}
> > >> +
> > >> +	if ((rc->Width != 0) && (rc->Height != 0) && ((fmt->formatFlags & StringFormatFlagsNoClip) == 0)) {
> > >> +// g_warning ("\tclip [%g %g %g %g]", rc->X, rc->Y, rc->Width, rc->Height);
> > >> +		/* We do not call cairo_reset_clip because we want to take previous clipping into account */
> > >> +		/* Use rc instead of frame variables because this is pre-transform */
> > >> +		gdip_cairo_rectangle (graphics, rc->X, rc->Y, rc->Width, rc->Height, TRUE);
> > >> +		cairo_clip (graphics->ct);
> > >> +	}
> > >>  	
> > >> -	if (fmt->formatFlags & StringFormatFlagsDirectionRightToLeft) {
> > >> -		/* with GDI+ the API not the renderer makes the direction decision */
> > >> -		pango_layout_set_auto_dir (layout, FALSE);
> > >> -		pango_context_set_base_dir (context, PANGO_DIRECTION_RTL);
> > >> +	/* with GDI+ the API not the renderer makes the direction decision */
> > >> +	pango_layout_set_auto_dir (layout, FALSE);
> > >> +	if (!(fmt->formatFlags & StringFormatFlagsDirectionRightToLeft) != !(fmt->formatFlags & StringFormatFlagsDirectionVertical)) {
> > >> +		pango_context_set_base_dir (context, PANGO_DIRECTION_WEAK_RTL);
> > >>  		pango_layout_context_changed (layout);
> > >>  
> > >>  		/* horizontal alignment */
> > >> @@ -141,6 +257,8 @@
> > >>  			break;
> > >>  		}
> > >>  	} else {
> > >> +		/* pango default base dir is WEAK_LTR, which is what we want */
> > >> +
> > >>  		/* horizontal alignment */
> > >>  		switch (fmt->alignment) {
> > >>  		case StringAlignmentNear:
> > >> @@ -158,9 +276,18 @@
> > >>  #ifdef PANGO_VERSION_CHECK
> > >>  #if PANGO_VERSION_CHECK(1,16,0)
> > >>  	if (fmt->formatFlags & StringFormatFlagsDirectionVertical) {
> > >> +		if (fmt->formatFlags & StringFormatFlagsDirectionRightToLeft) {
> > >> +			cairo_rotate (graphics->ct, M_PI/2.0);
> > >> +			cairo_translate (graphics->ct, 0, -FrameHeight);
> > >> +			pango_cairo_update_context (graphics->ct, context);
> > >> +		} else {
> > >> +			cairo_rotate (graphics->ct, 3.0*M_PI/2.0);
> > >> +			cairo_translate (graphics->ct, -FrameWidth, 0);
> > >> +			pango_cairo_update_context (graphics->ct, context);
> > >> +		}
> > >>  		/* only since Pango 1.16 */
> > >> -		pango_context_set_base_gravity (context, PANGO_GRAVITY_EAST);
> > >> -		pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_STRONG);
> > >> +		pango_context_set_base_gravity (context, PANGO_GRAVITY_AUTO);
> > >> +		pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_LINE);
> > >>  		pango_layout_context_changed (layout);
> > >>  	}
> > >>  #endif
> > >> @@ -169,38 +296,27 @@
> > >>  	/* TODO - StringFormatFlagsDisplayFormatControl
> > >>  		scan and replace them ??? */
> > >>  
> > >> -	/* TODO - StringFormatFlagsLineLimit */
> > >> -
> > >> -	if ((rc->Width != 0) && (rc->Height != 0) && ((fmt->formatFlags & StringFormatFlagsNoClip) == 0)) {
> > >> -//g_warning ("\tclip [%g %g %g %g]", rc->X, rc->Y, rc->Width, rc->Height);
> > >> -		/* We do not call cairo_reset_clip because we want to take previous clipping into account */
> > >> -		cairo_rectangle (ct, rc->X, rc->Y, rc->Width + 0.5, rc->Height + 0.5);
> > >> -		cairo_clip (ct);
> > >> -	}
> > >> -
> > >> +	/* Trimming options seem to apply only to the end of the string - gdi+ will still wrap 
> > >> +	 * with preference to word first, then character.  Unfortunately, pango doesn't have
> > >> +	 * any way to differentiate wrapping behavior from trimming behavior that I could find */
> > >> +	pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
> > >>  	switch (fmt->trimming) {
> > >>  	case StringTrimmingNone:
> > >> -		pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
> > >>  		pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE);
> > >>  		break;
> > >>  	case StringTrimmingCharacter:
> > >> -		pango_layout_set_wrap (layout, PANGO_WRAP_CHAR);
> > >>  		pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE);
> > >>  		break;
> > >>  	case StringTrimmingWord:
> > >> -		pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
> > >>  		pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE);
> > >>  		break;
> > >>  	case StringTrimmingEllipsisCharacter:
> > >> -		pango_layout_set_wrap (layout, PANGO_WRAP_CHAR);
> > >>  		pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
> > >>  		break;
> > >>  	case StringTrimmingEllipsisWord:
> > >> -		pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
> > >>  		pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
> > >>  		break;
> > >>  	case StringTrimmingEllipsisPath:
> > >> -		pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
> > >>  		pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_MIDDLE);
> > >>  		break;
> > >>  	}
> > >> @@ -233,59 +349,138 @@
> > >>  		}
> > >>  	}
> > >>  
> > >> +	if (fmt->numtabStops > 0) {
> > >> +		float tabPosition;
> > >> +		tabs = pango_tab_array_new (fmt->numtabStops, TRUE);
> > >> +		tabPosition = fmt->firstTabOffset;
> > >> +		for (i = 0; i < fmt->numtabStops; i++) {
> > >> +			tabPosition += fmt->tabStops[i];
> > >> +			pango_tab_array_set_tab (tabs, i, PANGO_TAB_LEFT, (gint)min (tabPosition, PANGO_MAX) * PANGO_SCALE);
> > >> +		}
> > >> +		pango_layout_set_tabs (layout, tabs);
> > >> +		pango_tab_array_free (tabs);
> > >> +	}
> > >> +
> > >> +	//g_warning ("length before ws removal: %d", length);
> > >> +	trimSpace = (fmt->formatFlags & StringFormatFlagsMeasureTrailingSpaces) == 0;
> > >>  	switch (fmt->hotkeyPrefix) {
> > >>  	case HotkeyPrefixHide:
> > >>  		/* we need to remove any accelerator from the string */
> > >> -		gdip_process_accelerators (text, length, NULL);
> > >> +		ftext = gdip_process_string (text, length, 1, trimSpace, NULL, charsRemoved);
> > >>  		break;
> > >>  	case HotkeyPrefixShow:
> > >>  		/* optimization: is seems that we never see the hotkey when using an underline font */
> > >>  		if (font->style & FontStyleUnderline) {
> > >>  			/* so don't bother drawing it (and simply add the '&' character) */
> > >> -			gdip_process_accelerators (text, length, NULL);
> > >> +			ftext = gdip_process_string (text, length, 1, trimSpace, NULL, charsRemoved);
> > >>  		} else {
> > >>  			/* find accelerator and add attribute to the next character (unless it's the prefix too) */
> > >>  			if (!list)
> > >>  				list = gdip_get_layout_attributes (layout);
> > >> -			gdip_process_accelerators (text, length, list);
> > >> +			ftext = gdip_process_string (text, length, 1, trimSpace, list, charsRemoved);
> > >>  		}
> > >>  		break;
> > >>  	default:
> > >> +		ftext = gdip_process_string (text, length, 0, trimSpace, NULL, charsRemoved);
> > >>  		break;
> > >>  	}
> > >> +	length = ftext->len;
> > >> +	//g_warning ("length after ws removal: %d", length);
> > >>  
> > >>  	if (list) {
> > >>  		pango_layout_set_attributes (layout, list);
> > >>  		pango_attr_list_unref (list);
> > >>  	}
> > >>  
> > >> -	pango_layout_set_text (layout, text, length);
> > >> +// g_warning("\tftext>%s< (%d)", ftext->str, -1);
> > >> +	pango_layout_set_text (layout, ftext->str, ftext->len);
> > >>  	GdipFree (text);
> > >> +    g_string_free (ftext, TRUE);
> > > 
> > > ^ tab/space
> > > 
> > >>  
> > >> -	pango_layout_get_pixel_extents (layout, NULL, &logical);
> > >> -//g_warning ("\tlogical\t[x %d, y %d, w %d, h %d]", logical.x, logical.y, logical.width, logical.height);
> > >> +	/* Trim the text after the last line for ease of counting lines/characters */
> > >> +	/* Also prevents drawing whole lines outside the boundaries if NoClip was specified */
> > >> +	/* In case of pre-existing clipping, use smaller of clip rectangle or our specified height */
> > >> +	if (FrameHeight > 0) {
> > >> +		cairo_clip_extents (graphics->ct, &clipx1, &clipy1, &clipx2, &clipy2);
> > >> +		if (clipy2 > 0)
> > >> +			clipy2 = min (clipy2, FrameHeight + FrameY);
> > >> +		else
> > >> +			clipy2 = FrameHeight + FrameY;
> > >> +		iter = pango_layout_get_iter (layout);
> > >> +		do {
> > >> +			if (iter == NULL)
> > >> +				break;
> > >> +			pango_layout_iter_get_line_yrange (iter, &y0, &y1);
> > >> +			//g_warning("yrange: %d  %d  clipy2: %f", y0 / PANGO_SCALE, y1 / PANGO_SCALE, clipy2);
> > >> +			/* StringFormatFlagsLineLimit */
> > >> +			if (((fmt->formatFlags & StringFormatFlagsLineLimit) && y1 / PANGO_SCALE > clipy2) || (y0 / PANGO_SCALE > clipy2)) {
> > >> +				PangoLayoutLine *line = pango_layout_iter_get_line_readonly (iter);
> > >> +				pango_layout_set_text (layout, pango_layout_get_text (layout), line->start_index);
> > >> +				break;
> > >> +			}
> > >> +		} while (pango_layout_iter_next_line (iter));
> > >> +		pango_layout_iter_free (iter);
> > >> +	}
> > >>  
> > >> -	box->X = rc->X;
> > >> -	box->Y = rc->Y;
> > >> -	box->Height = logical.height;
> > >> -	/* add an extra pixel for our AA hack + 2 more if we don't draw on the box itself */
> > >> -	box->Width = logical.width + (fmt->formatFlags & StringFormatFlagsNoFitBlackBox) ? 1 : 3;
> > >> -//g_warning ("\tbox\t[x %g, y %g, w %g, h %g]", box->X, box->Y, box->Width, box->Height);
> > >> +	pango_layout_get_pixel_extents (layout, &ink, &logical);
> > >> + //g_warning ("\tlogical\t[x %d, y %d, w %d, h %d][x %d, y %d, w %d, h %d]", logical.x, logical.y, logical.width, logical.height, ink.x, ink.y, ink.width, ink.height);
> > >>  
> > >> +	if ((fmt->formatFlags & StringFormatFlagsNoFitBlackBox) == 0) {
> > >> +		/* By default don't allow overhang - ink space may be larger than logical space */
> > >> +		if (fmt->formatFlags & StringFormatFlagsDirectionVertical) {
> > >> +			box->X = min (ink.y, logical.y);
> > >> +			box->Y = min (ink.x, logical.x);
> > >> +			box->Height = max (ink.width, logical.width);
> > >> +			box->Width = max (ink.height, logical.height);
> > >> +		} else {
> > >> +			box->X = min (ink.x, logical.x);
> > >> +			box->Y = min (ink.y, logical.y);
> > >> +			box->Height = max (ink.height, logical.height);
> > >> +			box->Width = max (ink.width, logical.width);
> > >> +		}
> > >> +	} else {
> > >> +		/* Allow overhang */
> > >> +		if (fmt->formatFlags & StringFormatFlagsDirectionVertical) {
> > >> +			box->X = logical.y;
> > >> +			box->Y = logical.x;
> > >> +			box->Height = logical.width;
> > >> +			box->Width = logical.height;
> > >> +		} else {
> > >> +			box->X = logical.x;
> > >> +			box->Y = logical.y;
> > >> +			box->Height = logical.height;
> > >> +			box->Width = logical.width;
> > >> +		}
> > >> +	}
> > >> + //g_warning ("\tbox\t[x %g, y %g, w %g, h %g]", box->X, box->Y, box->Width, box->Height);
> > >> +
> > >>  	/* vertical alignment*/
> > >> -	switch (fmt->lineAlignment) {
> > >> -	case StringAlignmentNear:
> > >> -		break;
> > >> -	case StringAlignmentCenter:
> > >> -		box->Y += (rc->Height - logical.height) / 2;
> > >> -		break;
> > >> -	case StringAlignmentFar:
> > >> -		box->Y += (rc->Height - logical.height);
> > >> -		break;
> > >> +	if (fmt->formatFlags & StringFormatFlagsDirectionVertical) {
> > >> +		switch (fmt->lineAlignment) {
> > >> +		case StringAlignmentNear:
> > >> +			break;
> > >> +		case StringAlignmentCenter:
> > >> +			box->X += (rc->Width - box->Width) / 2;
> > >> +			break;
> > >> +		case StringAlignmentFar:
> > >> +			box->X += (rc->Width - box->Width);
> > >> +			break;
> > >> +		}
> > >> +	} else {
> > >> +		switch (fmt->lineAlignment) {
> > >> +		case StringAlignmentNear:
> > >> +			break;
> > >> +		case StringAlignmentCenter:
> > >> +			box->Y += (rc->Height - box->Height) / 2;
> > >> +			break;
> > >> +		case StringAlignmentFar:
> > >> +			box->Y += (rc->Height - box->Height);
> > >> +			break;
> > >> +		}
> > >>  	}
> > >> -//g_warning ("va-box\t[x %g, y %g, w %g, h %g]", box->X, box->Y, box->Width, box->Height);
> > >> +// g_warning ("va-box\t[x %g, y %g, w %g, h %g]", box->X, box->Y, box->Width, box->Height);
> > >>  
> > >> -	pango_cairo_update_layout (ct, layout);
> > >> +	pango_cairo_update_layout (graphics->ct, layout);
> > >>  
> > >>  	return layout;
> > >>  }
> > >> @@ -297,22 +492,22 @@
> > >>  	PangoLayout *layout;
> > >>  	RectF box;
> > >>  
> > >> +	/* Setup cairo */
> > >> +	if (brush) {
> > >> +		gdip_brush_setup (graphics, brush);
> > >> +	} else {
> > >> +		cairo_set_source_rgb (graphics->ct, 0., 0., 0.);
> > >> +	}
> > >> +
> > >>  	cairo_save (graphics->ct);
> > >>  
> > >> -	layout = gdip_pango_setup_layout (graphics->ct, stringUnicode, length, font, rc, &box, format);
> > >> +	layout = gdip_pango_setup_layout (graphics, stringUnicode, length, font, rc, &box, format, NULL);
> > >>  	if (!layout) {
> > >>  		cairo_restore (graphics->ct);
> > >>  		return OutOfMemory;
> > >>  	}
> > >>  
> > >> -	/* Setup cairo */
> > >> -	if (brush) {
> > >> -		gdip_brush_setup (graphics, brush);
> > >> -	} else {
> > >> -		cairo_set_source_rgb (graphics->ct, 0., 0., 0.);
> > >> -	}
> > >> -
> > >> -	gdip_cairo_move_to (graphics, box.X, box.Y, FALSE, TRUE);
> > >> +	gdip_cairo_move_to (graphics, rc->X, rc->Y, FALSE, TRUE);
> > >>  	pango_cairo_show_layout (graphics->ct, layout);
> > >>  
> > >>  	g_object_unref (layout);
> > >> @@ -325,25 +520,96 @@
> > >>  	GDIPCONST GpStringFormat *format, RectF *boundingBox, int *codepointsFitted, int *linesFilled)
> > >>  {
> > >>  	PangoLayout *layout;
> > >> +	PangoLayoutLine *line;
> > >> +	PangoRectangle logical;
> > >> +	PangoLayoutIter *iter;
> > >> +	int *charsRemoved = NULL;
> > >>  
> > >>  	cairo_save (graphics->ct);
> > >>  
> > >> -	layout = gdip_pango_setup_layout (graphics->ct, stringUnicode, length, font, rc, boundingBox, format);
> > >> +	layout = gdip_pango_setup_layout (graphics, stringUnicode, length, font, rc, boundingBox, format, &charsRemoved);
> > >>  	if (!layout) {
> > >>  		cairo_restore (graphics->ct);
> > >>  		return OutOfMemory;
> > >>  	}
> > >>  		
> > >>  	if (codepointsFitted) {
> > >> -		// TODO - dummy (total) value returned
> > >> -		*codepointsFitted = length;
> > >> +		int charsFitted;
> > >> +		int lastIndex;
> > >> +		int y0;
> > >> +		int y1;
> > >> +		double min_x;
> > >> +		double max_x;
> > >> +		double max_y;
> > >> +		const char *layoutText;
> > >> +		if (boundingBox && format && (format->formatFlags & StringFormatFlagsDirectionVertical)) {
> > >> +			min_x = boundingBox->Y;
> > >> +			max_x = boundingBox->Y + boundingBox->Height;
> > >> +			max_y = boundingBox->X + boundingBox->Width;
> > >> +		} else if (boundingBox) {
> > >> +			min_x = boundingBox->X;
> > >> +			max_x = boundingBox->X + boundingBox->Width;
> > >> +			max_y = boundingBox->Y + boundingBox->Height;
> > >> +		} else if (format && (format->formatFlags & StringFormatFlagsDirectionVertical)) {
> > >> +			min_x = rc->Y;
> > >> +			max_x = rc->Y + rc->Height;
> > >> +			max_y = rc->X + rc->Width;
> > >> +		} else {
> > >> +			min_x = rc->X;
> > >> +			max_x = rc->X + rc->Width;
> > >> +			max_y = rc->Y + rc->Height;
> > >> +		}
> > >> +		lastIndex = 0;
> > >> +		iter = pango_layout_get_iter (layout);
> > >> +		do {
> > >> +			if (iter == NULL)
> > >> +				break;
> > >> +			pango_layout_iter_get_line_yrange (iter, &y0, &y1);
> > >> +			if (y0 / PANGO_SCALE >= max_y)
> > >> +				break;
> > >> +			if (pango_layout_iter_at_last_line (iter)) {
> > >> +				do {
> > >> +					pango_layout_iter_get_char_extents (iter, &logical);
> > >> +					/* check both max and min to catch right-to-left text, also width may be negative */
> > >> +					if ((logical.x / PANGO_SCALE > max_x || (logical.x + logical.width) / PANGO_SCALE > max_x) || (logical.x / PANGO_SCALE < min_x || (logical.x + logical.width) / PANGO_SCALE < min_x))
> > >> +						break;
> > >> +					lastIndex = pango_layout_iter_get_index (iter);
> > >> +				} while (pango_layout_iter_next_char (iter));
> > >> +				break;
> > >> +			} else {
> > >> +				line = pango_layout_iter_get_line_readonly (iter);
> > >> +				lastIndex = line->start_index + line->length - 1;
> > >> +			}
> > >> +		} while (pango_layout_iter_next_line (iter));
> > >> +		pango_layout_iter_free (iter);
> > >> +		layoutText = pango_layout_get_text (layout);
> > >> +		/* this can happen when the string ends in a newline */
> > >> +		if (lastIndex >= strlen (layoutText))
> > >> +			lastIndex = strlen (layoutText) - 1;
> > >> +		/* Add back in any & characters removed and the final newline characters (if any) */
> > >> +		charsFitted = g_utf8_strlen (layoutText, lastIndex + 1) + charsRemoved [lastIndex];
> > >> +		//g_warning("lastIndex: %d\t\tcharsRemoved: %d", lastIndex, charsRemoved[lastIndex]);
> > >> +		/* safe because of null termination */
> > >> +		switch (layoutText [lastIndex + 1]) {
> > >> +			case '\r':
> > >> +				charsFitted++;
> > >> +				if (layoutText [lastIndex + 2] == '\n')
> > >> +					charsFitted++;
> > >> +				break;
> > >> +			case '\n':
> > >> +				charsFitted++;
> > >> +				break;
> > >> +		}
> > >> +		*codepointsFitted = charsFitted;
> > >>  	}
> > >>  
> > >> +	GdipFree (charsRemoved);
> > >> +
> > >>  	if (linesFilled) {
> > >>  		*linesFilled = pango_layout_get_line_count (layout);
> > >> -//g_warning ("linesFilled %d", *linesFilled);
> > >> +// g_warning ("linesFilled %d", *linesFilled);
> > >>  	}
> > >> -//else g_warning ("linesFilled %d", pango_layout_get_line_count (layout));
> > >> +// else g_warning ("linesFilled %d", pango_layout_get_line_count (layout));
> > >>  
> > >>  	g_object_unref (layout);
> > >>  	cairo_restore (graphics->ct);
> > >> @@ -361,7 +627,7 @@
> > >>  
> > >>  	cairo_save (graphics->ct);
> > >>  
> > >> -	layout = gdip_pango_setup_layout (graphics->ct, stringUnicode, length, font, layoutRect, &boundingBox, format);
> > >> +	layout = gdip_pango_setup_layout (graphics, stringUnicode, length, font, layoutRect, &boundingBox, format, NULL);
> > >>  	if (!layout) {
> > >>  		cairo_restore (graphics->ct);
> > >>  		return OutOfMemory;
> > >> @@ -402,7 +668,16 @@
> > >>  			charRect.Y = (float)box.y / PANGO_SCALE;
> > >>  			charRect.Width = (float)box.width / PANGO_SCALE;
> > >>  			charRect.Height = (float)box.height / PANGO_SCALE;
> > >> -//g_warning ("[%d] [%d : %d-%d] %c [x %g y %g w %g h %g]", i, j, start, end, (char)stringUnicode[j], charRect.X, charRect.Y, charRect.Width, charRect.Height);
> > >> +			/* Normalize values (width/height can be negative) */
> > >> +			if (charRect.Width < 0) {
> > >> +				charRect.Width = -charRect.Width;
> > >> +				charRect.X -= charRect.Width;
> > >> +			}
> > >> +			if (charRect.Height < 0) {
> > >> +				charRect.Height = -charRect.Height;
> > >> +				charRect.Y -= charRect.Height;
> > >> +			}
> > >> +// g_warning ("[%d] [%d : %d-%d] %c [x %g y %g w %g h %g]", i, j, start, end, (char)stringUnicode[j], charRect.X, charRect.Y, charRect.Width, charRect.Height);
> > >>  			status = GdipCombineRegionRect (regions [i], &charRect, CombineModeUnion);
> > >>  			if (status != Ok)
> > >>  				break;
> > >> Index: src/graphics-path.c
> > >> ===================================================================
> > >> --- src/graphics-path.c	(revision 126715)
> > >> +++ src/graphics-path.c	(working copy)
> > >> @@ -1212,7 +1212,7 @@
> > >>  	PangoLayout* layout; 
> > >>  
> > >>  	cairo_save (cr);
> > >> -	layout = gdip_pango_setup_layout (cr, string, length, font, layoutRect, &box, format);
> > >> +	layout = gdip_pango_setup_layout (cr, string, length, font, layoutRect, &box, format, NULL);
> > >>  	pango_cairo_layout_path (cr, layout);
> > >>  	g_object_unref (layout);
> > >>  	cairo_restore (cr);
> > > 
> > > 
> > _______________________________________________
> > Mono-devel-list mailing list
> > Mono-devel-list at lists.ximian.com
> > http://lists.ximian.com/mailman/listinfo/mono-devel-list
> 



More information about the Mono-winforms-list mailing list