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.


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
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);
+
+       /* 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;
+       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);
 
-//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);
 
-       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-winforms-list maillist  -  Mono-winforms-list@lists.ximian.com
http://lists.ximian.com/mailman/listinfo/mono-winforms-list

Reply via email to