-
Notifications
You must be signed in to change notification settings - Fork 174
Pango fixes #384
Pango fixes #384
Changes from all commits
64742e3
18f7fb0
b8fc10e
78435ca
802b06d
47a0216
55012d3
3d7d3c8
fa81009
bcddd2a
7eac989
cbf8a50
e2e5e86
dc4ca4d
393311c
d4320b5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -77,7 +77,7 @@ gdip_set_array_values (int *array, int value, int num) | |
| static GString * | ||
| gdip_process_string (gchar *text, int length, int removeAccelerators, int trimSpace, PangoAttrList *list, int **charsRemoved) | ||
| { | ||
| int i, j; | ||
| int c, j, r; | ||
| int nonws = 0; | ||
| gchar *iter; | ||
| gchar *iter2; | ||
|
|
@@ -105,15 +105,16 @@ gdip_process_string (gchar *text, int length, int removeAccelerators, int trimSp | |
| } | ||
|
|
||
| iter = text; | ||
| i = 0; | ||
| j = 0; | ||
| // c: Characters handled in the inner loop, that might be removed. | ||
| j = 0; // Index in output | ||
| r = 0; // Characters removed | ||
| 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; | ||
| r++; | ||
| ch = g_utf8_get_char (iter); | ||
| /* add an attribute on the next character */ | ||
| if (list && (iter - text < length) && (ch != GDIP_WINDOWS_ACCELERATOR)) { | ||
|
|
@@ -126,43 +127,49 @@ gdip_process_string (gchar *text, int length, int removeAccelerators, int trimSp | |
| 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) */ | ||
| c = 1; | ||
| 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)) { | ||
| c = 0; | ||
| g_string_append_len (res, iter, iter2 - iter); | ||
| if (charsRemoved && *charsRemoved) | ||
| gdip_set_array_values ((*charsRemoved)+j, i - j, iter2 - iter); | ||
| gdip_set_array_values ((*charsRemoved)+j, r, iter2 - iter); | ||
| j += iter2 - iter; | ||
| break; | ||
| } | ||
| c++; | ||
| } | ||
| i += iter2 - iter; | ||
| r += c; | ||
| 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; | ||
| r += length - (iter - text); | ||
| 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); | ||
| gdip_set_array_values ((*charsRemoved)+j, r, 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 (!nonws && trimSpace && length > 0) { | ||
| iter = text; | ||
| iter2 = g_utf8_next_char (iter); | ||
| g_string_append_len (res, iter, iter2 - iter); | ||
| j += iter2 - iter; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be possible to make simple unit test for this? Just so we can compare the behavior on Windows and Linux.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be, measure a string with, for example, an em-space (the issue I had). I'll have a go tomorrow.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, doing that was easy enough, but it uncovered something else – the reported number of characters fitted is incorrect when removing multi-byte code points, or removing immediately after them. This change does not make it wrong, it just alters the (incorrect) behaviour slightly.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, I hope I've sorted out counting the characters. But I did a few checks on Windows (via .Net) as to the behaviour there, and it is weird. To me, it looks like unless there is a line ending, the only whitespace removed from the end are actual spaces. If there is a line ending, all trailing whitespace (considering tab as non-whitespace!) is removed. Furthermore, there is no leaving one space / character behind (gives a width of 0). It'd be good to do some testing without .Net in between, although I doubt it'd change. And I'm sure using Cairo text rendering doesn't follow Windows any more than it does here.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| r--; | ||
| } | ||
| 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); | ||
| gdip_set_array_values (*charsRemoved + prevj, r, j - prevj); | ||
| } | ||
| return res; | ||
| } | ||
|
|
@@ -187,10 +194,6 @@ gdip_pango_setup_layout (cairo_t *cr, GDIPCONST WCHAR *stringUnicode, int length | |
| 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 */ | ||
| BOOL use_horizontal_layout; | ||
|
|
||
|
|
@@ -427,23 +430,14 @@ gdip_pango_setup_layout (cairo_t *cr, GDIPCONST WCHAR *stringUnicode, int length | |
| /* 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) { | ||
| clipy2 = FrameHeight; | ||
| if (!(fmt->formatFlags & StringFormatFlagsNoClip)) { | ||
| cairo_clip_extents (cr, &clipx1, &clipy1, &clipx2, &clipy2); | ||
| if (fmt->formatFlags & StringFormatFlagsDirectionVertical) { | ||
| clipy2 = min (clipx2 - rc->X, FrameWidth); | ||
| } else { | ||
| clipy2 = min (clipy2 - rc->Y, FrameHeight); | ||
| } | ||
| } | ||
| 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); | ||
| //g_warning("yrange: %d %d FrameHeight: %f", y0 / PANGO_SCALE, y1 / PANGO_SCALE, FrameHeight); | ||
| /* StringFormatFlagsLineLimit */ | ||
| if (((fmt->formatFlags & StringFormatFlagsLineLimit) && y1 / PANGO_SCALE >= clipy2) || (y0 / PANGO_SCALE >= clipy2)) { | ||
| if (((fmt->formatFlags & StringFormatFlagsLineLimit) && y1 / PANGO_SCALE > FrameHeight) || (y0 / PANGO_SCALE >= FrameHeight)) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, changed the second contition to use FrameHeight too, and eliminated clipy2 and co. Looking further, clipy2 and co were of very limited reliability for MeasureString because when used from managed code, the layout rectangle will have either the location or the size, not both. This code only does anything when the layout rectangle has size information, therefore there is a good chance that basing anything on the clip rectangle is meaningless. Drawing would probably not be affected. |
||
| PangoLayoutLine *line = pango_layout_iter_get_line_readonly (iter); | ||
| pango_layout_set_text (layout, pango_layout_get_text (layout), line->start_index); | ||
|
|
||
|
|
@@ -590,6 +584,7 @@ pango_MeasureString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int l | |
| int lastIndex; | ||
| int y0; | ||
| int y1; | ||
| int len; | ||
| double min_x; | ||
| double max_x; | ||
| double max_y; | ||
|
|
@@ -609,7 +604,7 @@ pango_MeasureString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int l | |
| if (iter == NULL) | ||
| break; | ||
| pango_layout_iter_get_line_yrange (iter, &y0, &y1); | ||
| if (y0 / PANGO_SCALE >= max_y) | ||
| if ((format && (format->formatFlags & StringFormatFlagsLineLimit) && y1 / PANGO_SCALE > max_y) || y0 / PANGO_SCALE >= max_y) | ||
| break; | ||
| lines++; | ||
| if (pango_layout_iter_at_last_line (iter)) { | ||
|
|
@@ -630,22 +625,32 @@ pango_MeasureString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int l | |
|
|
||
| if (codepointsFitted) { | ||
| 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') | ||
|
|
||
| if (lines > 0) | ||
| len = strlen (layoutText); | ||
|
|
||
| if (lines > 0 && len > 0) { | ||
| /* this can happen when the string ends in a newline */ | ||
| if (lastIndex >= len) { | ||
| lastIndex = g_utf8_prev_char(layoutText + len) - layoutText; | ||
| } | ||
| /* Add back in any & characters removed and the final newline characters (if any) */ | ||
| charsFitted = g_utf8_strlen (layoutText, g_utf8_next_char (layoutText + lastIndex) - layoutText) + charsRemoved [lastIndex]; | ||
| //g_warning("lastIndex: %d\t\tcharsRemoved: %d", lastIndex, charsRemoved[lastIndex]); | ||
| /* safe because of null termination */ | ||
| switch (layoutText [lastIndex + 1]) { | ||
| case '\r': | ||
| charsFitted++; | ||
| break; | ||
| case '\n': | ||
| charsFitted++; | ||
| break; | ||
| if (layoutText [lastIndex + 2] == '\n') | ||
| charsFitted++; | ||
| break; | ||
| case '\n': | ||
| charsFitted++; | ||
| break; | ||
| } | ||
| } else { | ||
| // Nothing was fitted. Most likely either the input length was zero or LineLimit prevented fitting any lines (the height of the first line is greater than the height of the bounding box). | ||
| charsFitted = 0; | ||
| } | ||
| *codepointsFitted = charsFitted; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately this won't work on the Pango CoreText backend. There's a bug in Pango where the point size is treated as CoreText point size, which is actually not the typographic point size, but something that HTML calls "px" (device independent pixel). One of them 1/72 inch, the other one is 1/96 inch.
Then again, it was broken before, the bug is in Cairo and not here, and the FreeType backend is currently enforced on all platforms anyway... so just a random note :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would pango_font_description_set_absolute_size() (meant to be the size in device units) solve that? I was in two minds about whether it was better to calculate the point size (which I did in the end), or to use that and the pixel size which is already computed on creating the font...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incidentally it would because the CoreText backend doesn't differentiate them properly, so it would cancel out the bug. I am not sure what is a better option, but I am fine with either one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I settled on the way I did because of the possibility that the output dpi was different to the display dpi (e.g. for a printer). I realise this does not solve that completely, but it does mean the only unit affected is Pixel. I guess the correct thing to do would be to pass in the dpi to use. That'd require a change in #383 (or moving those changes) to set the resolution on the Graphics object used when drawing to a GraphicsPath.