I mentioned earlier that I was doing work to get pango support working
properly in libgdiplus. Here's a patch with my work so far. Most of
the formatting options are supported, but some of them aren't (like
digit substitution), and some are not fully supported (like certain
string trimming behavior). I did my best to match gdi+ behavior as seen
on Windows for things like white space trimming, new lines, wrapping,
etc. I've tested most of the options that I worked on with the
exception of tab stops.
Let me know what you think.
I'm getting ready to do work on the TextBox control to get it to handle
complex scripts better as well. This will most likely end up in putting
some pango and uniscribe (for Windows) calls in the TextBoxTextRenderer
class. One question I have about this, is would it be possible to have
the Windows TextBox use pango? I know that pango on windows will pass
things through to uniscribe, and it would be easier to not have to use
two different text rendering backends if possible.
Jonathan Anderson
Index: src/text-pango-private.h
===================================================================
--- src/text-pango-private.h (revision 125661)
+++ 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 125661)
+++ 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,49 @@
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 (fmt->formatFlags & StringFormatFlagsNoWrap)
+ pango_layout_set_single_paragraph_mode (layout, TRUE);
+
+ 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 +260,8 @@
break;
}
} else {
+ /* pango default base dir is WEAK_LTR, which is what we want */
+
/* horizontal alignment */
switch (fmt->alignment) {
case StringAlignmentNear:
@@ -158,8 +279,17 @@
#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_base_gravity (context, PANGO_GRAVITY_AUTO);
pango_context_set_gravity_hint (context,
PANGO_GRAVITY_HINT_STRONG);
pango_layout_context_changed (layout);
}
@@ -169,38 +299,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 +352,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 +495,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 +523,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 +630,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 +671,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 125661)
+++ 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