[Mono-dev] GDI+ and Pango.

Miguel de Icaza miguel at novell.com
Tue May 12 20:06:32 EDT 2009


Hello,

    There was a discussion today on IRC about Winforms and Pango, and I
was wondering if this patch ever got committed, and in general what was
the status of using Pango for GDI+

> 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-devel-list mailing list