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