From 8a7914eb21bf6ddffcd1db84378cb6b52ad925c8 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sun, 1 Apr 2018 11:26:22 +0200 Subject: [PATCH 1/4] Test GdipMeasureCharacterRanges output for alignment. --- tests/testtext.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/testtext.c b/tests/testtext.c index 55a34a7c5..659b0f8f2 100644 --- a/tests/testtext.c +++ b/tests/testtext.c @@ -117,8 +117,10 @@ static void test_measure_string_alignment(void) GpFont *font; GpStatus status; GpRectF rect, bounds; + GpRegion *region; const WCHAR teststring1[] = { 'M', 0 }; INT i; + static const CharacterRange character_range = { 0, 1 }; static const struct test_data { INT flags; @@ -167,12 +169,16 @@ static void test_measure_string_alignment(void) expect (Ok, status); status = GdipCreateFont (family, 10, FontStyleRegular, UnitPixel, &font); expect (Ok, status); + status = GdipCreateRegion (®ion); + expect (Ok, status); status = GdipCreateBitmapFromScan0 (400, 400, 0, PixelFormat32bppRGB, NULL, (GpBitmap **) &image); expect (Ok, status); status = GdipGetImageGraphicsContext (image, &graphics); expect (Ok, status); ok (graphics != NULL, "Expected graphics to be initialized\n"); + GdipSetStringFormatMeasurableCharacterRanges (format, 1, &character_range); + for (i = 0; i < sizeof(td) / sizeof(td[0]); i++) { GdipSetStringFormatFlags (format, td[i].flags); GdipSetStringFormatAlign (format, td[i].alignment); @@ -189,6 +195,15 @@ static void test_measure_string_alignment(void) expectf_ (td[i].y_y0 + td[i].y_yy * bounds.Height + 10.0, bounds.Y, 0.6); expectf_ (td[i].right_x0 + td[i].right_xx * bounds.Width + 5.0, bounds.X + bounds.Width, 0.6); expectf_ (td[i].bottom_y0 + td[i].bottom_yy * bounds.Height + 10.0, bounds.Y + bounds.Height, 0.6); + + status = GdipMeasureCharacterRanges (graphics, teststring1, 1, font, &rect, format, 1, ®ion); + expect (Ok, status); + status = GdipGetRegionBounds (region, graphics, &bounds); + expect (Ok, status); + expectf_ (td[i].x_x0 + td[i].x_xx * bounds.Width + 5.0, bounds.X, 2.6); + expectf_ (td[i].y_y0 + td[i].y_yy * bounds.Height + 10.0, bounds.Y, 2.6); + expectf_ (td[i].right_x0 + td[i].right_xx * bounds.Width + 5.0, bounds.X + bounds.Width, 2.6); + expectf_ (td[i].bottom_y0 + td[i].bottom_yy * bounds.Height + 10.0, bounds.Y + bounds.Height, 2.6); } GdipDeleteGraphics (graphics); @@ -196,6 +211,7 @@ static void test_measure_string_alignment(void) GdipDeleteFontFamily (family); GdipDeleteStringFormat (format); GdipDisposeImage (image); + GdipDeleteRegion (region); } #endif From 381265b4ba42c196f0e27f372217455e46a7d296 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sun, 1 Apr 2018 11:52:34 +0200 Subject: [PATCH 2/4] Fix Pango text measuring to pass the test. --- src/text-pango.c | 17 +++++++++++++---- tests/testtext.c | 8 ++++---- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/text-pango.c b/src/text-pango.c index e42418911..89a063fa4 100644 --- a/src/text-pango.c +++ b/src/text-pango.c @@ -714,10 +714,17 @@ pango_MeasureCharacterRanges (GpGraphics *graphics, GDIPCONST WCHAR *stringUnico GpRectF charRect; pango_layout_index_to_pos (layout, idxUtf8, &box); - charRect.X = (float)box.x / PANGO_SCALE + box_offset.X; - charRect.Y = (float)box.y / PANGO_SCALE + box_offset.Y; - charRect.Width = (float)box.width / PANGO_SCALE; - charRect.Height = (float)box.height / PANGO_SCALE; + if (format->formatFlags & StringFormatFlagsDirectionVertical) { + charRect.X = (float)box.y / PANGO_SCALE; + charRect.Y = (float)box.x / PANGO_SCALE; + charRect.Width = (float)box.height / PANGO_SCALE; + charRect.Height = (float)box.width / PANGO_SCALE; + } else { + charRect.X = (float)box.x / PANGO_SCALE; + charRect.Y = (float)box.y / PANGO_SCALE; + charRect.Width = (float)box.width / PANGO_SCALE; + charRect.Height = (float)box.height / PANGO_SCALE; + } /* Normalize values (width/height can be negative) */ if (charRect.Width < 0) { charRect.Width = -charRect.Width; @@ -727,6 +734,8 @@ pango_MeasureCharacterRanges (GpGraphics *graphics, GDIPCONST WCHAR *stringUnico charRect.Height = -charRect.Height; charRect.Y -= charRect.Height; } + charRect.X += box_offset.X + layoutRect->X; + charRect.Y += box_offset.Y + layoutRect->Y; // 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) diff --git a/tests/testtext.c b/tests/testtext.c index 659b0f8f2..11f9cc66d 100644 --- a/tests/testtext.c +++ b/tests/testtext.c @@ -200,10 +200,10 @@ static void test_measure_string_alignment(void) expect (Ok, status); status = GdipGetRegionBounds (region, graphics, &bounds); expect (Ok, status); - expectf_ (td[i].x_x0 + td[i].x_xx * bounds.Width + 5.0, bounds.X, 2.6); - expectf_ (td[i].y_y0 + td[i].y_yy * bounds.Height + 10.0, bounds.Y, 2.6); - expectf_ (td[i].right_x0 + td[i].right_xx * bounds.Width + 5.0, bounds.X + bounds.Width, 2.6); - expectf_ (td[i].bottom_y0 + td[i].bottom_yy * bounds.Height + 10.0, bounds.Y + bounds.Height, 2.6); + expectf_ (td[i].x_x0 + td[i].x_xx * bounds.Width + 5.0, bounds.X, 3.0); + expectf_ (td[i].y_y0 + td[i].y_yy * bounds.Height + 10.0, bounds.Y, 3.0); + expectf_ (td[i].right_x0 + td[i].right_xx * bounds.Width + 5.0, bounds.X + bounds.Width, 3.0); + expectf_ (td[i].bottom_y0 + td[i].bottom_yy * bounds.Height + 10.0, bounds.Y + bounds.Height, 3.0); } GdipDeleteGraphics (graphics); From b1908c9096f7a84dfa4a0020e3baeeb54613fc12 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sun, 1 Apr 2018 13:18:56 +0200 Subject: [PATCH 3/4] Move Cairo text alignment code from DrawString to MeasureString and fix up MeasureCharacterRanges to return correct coordinates. Also simplify and fix hotkey underline drawing. --- src/text-cairo.c | 137 +++++++++++++++++++---------------------------- tests/testtext.c | 8 +-- 2 files changed, 59 insertions(+), 86 deletions(-) diff --git a/src/text-cairo.c b/src/text-cairo.c index 27e2838b4..aef18712a 100644 --- a/src/text-cairo.c +++ b/src/text-cairo.c @@ -727,6 +727,44 @@ MeasureString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int *length *linesFilled = floor (height / LineHeight); } + if (AlignHorz != StringAlignmentNear || AlignVert != StringAlignmentNear) { + // Update alignment + int length = 0; + int current_line_length = 0; + for (i = 0; i < StringLen; i++) { + if (i == current_line_length) { + length = StringDetails[i].LineLen; + current_line_length = min(length + i, StringLen); + } + + switch (AlignHorz) { + case StringAlignmentNear: + break; + case StringAlignmentCenter: + if ((current_line_length == 1) || (StringDetails [current_line_length - 1].PosX > 0)) { + StringDetails[i].PosX += (FrameWidth - StringDetails [current_line_length - 1].PosX - + StringDetails [current_line_length - 1].Width) / 2; + } + break; + case StringAlignmentFar: + StringDetails[i].PosX += FrameWidth - StringDetails [current_line_length - 1].PosX - + StringDetails [current_line_length - 1].Width; + break; + } + + switch (AlignVert) { + case StringAlignmentNear: + break; + case StringAlignmentCenter: + StringDetails[i].PosY += (FrameHeight - MaxY) / 2; + break; + case StringAlignmentFar: + StringDetails[i].PosY += FrameHeight - MaxY; + break; + } + } + } + /* if asked, supply extra data to be reused when drawing the same string */ if (data) { data->align_horz = AlignHorz; @@ -813,55 +851,13 @@ DrawString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int length, GD if ((fmt->formatFlags & StringFormatFlagsDirectionVertical)==0) { CursorX = rc->X + StringDetails[i].PosX; - switch (AlignHorz) { - case StringAlignmentNear: - break; - case StringAlignmentCenter: - /* PosX isn't calculated if the char is out of the bounding box (#79573/#79685) */ - if ((current_line_length == 1) || (StringDetails [current_line_length-1].PosX > 0)) { - CursorX += (rc->Width - StringDetails [current_line_length-1].PosX - - StringDetails [current_line_length-1].Width) / 2; - } - /* which means that the line is too long so no centering is required */ - break; - case StringAlignmentFar: - CursorX += rc->Width - StringDetails [current_line_length-1].PosX - - StringDetails [current_line_length-1].Width; - break; - } - - switch (AlignVert) { - case StringAlignmentNear: CursorY=rc->Y+StringDetails[i].PosY+LineHeight; break; - case StringAlignmentCenter: CursorY=rc->Y+(rc->Height-MaxY)/2+StringDetails[i].PosY+LineHeight; break; - case StringAlignmentFar: CursorY=rc->Y+rc->Height-MaxY+StringDetails[i].PosY+LineHeight; break; - } + CursorY = rc->Y + StringDetails[i].PosY + LineHeight; gdip_cairo_move_to (graphics, CursorX, CursorY, FALSE, TRUE); cairo_show_text (graphics->ct, (const char *) String); } else { - CursorY = rc->Y; - switch (AlignHorz) { - case StringAlignmentNear: - break; - case StringAlignmentCenter: - /* PosX isn't calculated if the char is out of the bounding box (#79573/#79685) */ - if ((current_line_length == 1) || (StringDetails [current_line_length-1].PosX > 0)) { - CursorY += (rc->Height - StringDetails[current_line_length-1].PosX - - StringDetails [current_line_length-1].Width) / 2; - } - /* which means that the line is too long so no centering is required */ - break; - case StringAlignmentFar: - CursorY += rc->Height - StringDetails[current_line_length-1].PosX - - StringDetails [current_line_length-1].Width; - break; - } - - switch (AlignVert) { - case StringAlignmentNear: CursorX=rc->X + StringDetails[i].PosX+StringDetails[i].PosY; break; - case StringAlignmentCenter: CursorX=rc->X + StringDetails[i].PosX+(rc->Width-MaxY)/2+StringDetails[i].PosY; break; - case StringAlignmentFar: CursorX=rc->X + StringDetails[i].PosX+rc->Width-MaxY+StringDetails[i].PosY; break; - } + CursorY = rc->Y + StringDetails[i].PosX; + CursorX = rc->X + StringDetails[i].PosY; /* Rotate text for vertical drawing */ cairo_save (graphics->ct); @@ -915,48 +911,23 @@ DrawString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int length, GD if (fmt->hotkeyPrefix==HotkeyPrefixShow && data->has_hotkeys) { GpStringDetailStruct *CurrentDetail = StringDetails; for (i=0; iFlags & STRING_DETAIL_LINESTART) { + if (CurrentDetail->Flags & STRING_DETAIL_HOTKEY) { if ((fmt->formatFlags & StringFormatFlagsDirectionVertical)==0) { - switch (AlignHorz) { - case StringAlignmentNear: CursorX=rc->X; break; - case StringAlignmentCenter: CursorX=rc->X+(rc->Width-StringDetails[i+StringDetails[i].LineLen-1].PosX-StringDetails[i+StringDetails[i].LineLen-1].Width)/2; break; - case StringAlignmentFar: CursorX=rc->X+rc->Width-StringDetails[i+StringDetails[i].LineLen-1].PosX-StringDetails[i+StringDetails[i].LineLen-1].Width; break; - } - - switch (AlignVert) { - case StringAlignmentNear: CursorY=rc->Y+StringDetails[i].PosY+LineHeight; break; - case StringAlignmentCenter: CursorY=rc->Y+(rc->Height-MaxY)/2+StringDetails[i].PosY+LineHeight; break; - case StringAlignmentFar: CursorY=rc->Y+rc->Height-MaxY+StringDetails[i].PosY+LineHeight; break; - } + CursorX = rc->X + StringDetails[i].PosX; + CursorY = rc->Y + StringDetails[i].PosY + LineHeight; } else { - switch (AlignHorz) { - case StringAlignmentNear: CursorY=rc->Y; break; - case StringAlignmentCenter: CursorY=rc->Y+(rc->Height-StringDetails[i+StringDetails[i].LineLen-1].PosX-StringDetails[i+StringDetails[i].LineLen-1].Width)/2; break; - case StringAlignmentFar: CursorY=rc->Y+rc->Height-StringDetails[i+StringDetails[i].LineLen-1].PosX-StringDetails[i+StringDetails[i].LineLen-1].Width; break; - } - - switch (AlignVert) { - case StringAlignmentNear: CursorX=rc->X+StringDetails[i].PosY; break; - case StringAlignmentCenter: CursorX=rc->X+(rc->Width-MaxY)/2+StringDetails[i].PosY; break; - case StringAlignmentFar: CursorX=rc->X+rc->Width-MaxY+StringDetails[i].PosY; break; - } + CursorY = rc->Y + StringDetails[i].PosX; + CursorX = rc->X + StringDetails[i].PosY; } - } - if (CurrentDetail->Flags & STRING_DETAIL_HOTKEY) { if ((fmt->formatFlags & StringFormatFlagsDirectionVertical)==0) { - CursorX+=CurrentDetail->PosX; cairo_set_line_width(graphics->ct, 1); - - gdip_cairo_move_to (graphics, (int)(CursorX), (int)(CursorY+FontExtent.descent/2), FALSE, TRUE); - gdip_cairo_line_to (graphics, (int)(CursorX+CurrentDetail->Width), (int)(CursorY+FontExtent.descent/2), FALSE, TRUE); + gdip_cairo_move_to (graphics, (int)(CursorX), (int)(CursorY+FontExtent.descent), FALSE, TRUE); + gdip_cairo_line_to (graphics, (int)(CursorX+CurrentDetail->Width), (int)(CursorY+FontExtent.descent), FALSE, TRUE); cairo_stroke (graphics->ct); - CursorX-=CurrentDetail->PosX; } else { - CursorY+=CurrentDetail->PosX; - gdip_cairo_move_to (graphics, (int)(CursorX-FontExtent.descent/2), (int)(CursorY), FALSE, TRUE); - gdip_cairo_line_to (graphics, (int)(CursorX-FontExtent.descent/2), (int)(CursorY+CurrentDetail->Width), FALSE, TRUE); - CursorY-=CurrentDetail->PosX; + gdip_cairo_move_to (graphics, (int)(CursorX-FontExtent.descent), (int)(CursorY), FALSE, TRUE); + gdip_cairo_line_to (graphics, (int)(CursorX-FontExtent.descent), (int)(CursorY+CurrentDetail->Width), FALSE, TRUE); } } CurrentDetail++; @@ -1164,13 +1135,13 @@ cairo_MeasureCharacterRanges (GpGraphics *graphics, GDIPCONST WCHAR *stringUnico continue; if (format->formatFlags & StringFormatFlagsDirectionVertical) { - charRect.X = StringDetails [j].PosY; - charRect.Y = StringDetails [j].PosX; + charRect.X = layoutRect->X + StringDetails [j].PosY; + charRect.Y = layoutRect->Y + StringDetails [j].PosX; charRect.Width = lineHeight; charRect.Height = StringDetails [j].Width; } else { - charRect.X = StringDetails [j].PosX; - charRect.Y = StringDetails [j].PosY; + charRect.X = layoutRect->X + StringDetails [j].PosX; + charRect.Y = layoutRect->Y + StringDetails [j].PosY; charRect.Width = StringDetails [j].Width; charRect.Height = lineHeight; } diff --git a/tests/testtext.c b/tests/testtext.c index 11f9cc66d..8023d8aa9 100644 --- a/tests/testtext.c +++ b/tests/testtext.c @@ -108,6 +108,8 @@ static void test_measure_string(void) GdipDisposeImage (image); } +#endif + static void test_measure_string_alignment(void) { GpStringFormat *format; @@ -189,12 +191,14 @@ static void test_measure_string_alignment(void) rect.Width = 200.0; rect.Height = 100.0; set_rect_empty (&bounds); +#ifdef USE_PANGO_RENDERING status = GdipMeasureString (graphics, teststring1, 1, font, &rect, format, &bounds, NULL, NULL); expect (Ok, status); expectf_ (td[i].x_x0 + td[i].x_xx * bounds.Width + 5.0, bounds.X, 0.6); expectf_ (td[i].y_y0 + td[i].y_yy * bounds.Height + 10.0, bounds.Y, 0.6); expectf_ (td[i].right_x0 + td[i].right_xx * bounds.Width + 5.0, bounds.X + bounds.Width, 0.6); expectf_ (td[i].bottom_y0 + td[i].bottom_yy * bounds.Height + 10.0, bounds.Y + bounds.Height, 0.6); +#endif status = GdipMeasureCharacterRanges (graphics, teststring1, 1, font, &rect, format, 1, ®ion); expect (Ok, status); @@ -214,8 +218,6 @@ static void test_measure_string_alignment(void) GdipDeleteRegion (region); } -#endif - int main (int argc, char**argv) { @@ -223,8 +225,8 @@ main (int argc, char**argv) #if defined(USE_PANGO_RENDERING) || defined(USE_WINDOWS_GDIPLUS) test_measure_string (); - test_measure_string_alignment (); #endif + test_measure_string_alignment (); SHUTDOWN; return 0; From c6038b7b6a70a595c8b85c4b140b3308ba732cd5 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sun, 1 Apr 2018 13:29:04 +0200 Subject: [PATCH 4/4] Apply alignment to bounding box in cairo_MeasureString. --- src/text-cairo.c | 34 ++++++++++++++++++++++++++++++++++ tests/testtext.c | 2 -- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/text-cairo.c b/src/text-cairo.c index aef18712a..2419c00bb 100644 --- a/src/text-cairo.c +++ b/src/text-cairo.c @@ -705,6 +705,40 @@ MeasureString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int *length boundingBox->Width = gdip_convgr_unitx (graphics, boundingBox->Width); boundingBox->Height = gdip_convgr_unity (graphics, boundingBox->Height); } + + switch (AlignVert) { + case StringAlignmentCenter: + if (format->formatFlags & StringFormatFlagsDirectionVertical) { + boundingBox->X += (FrameHeight - MaxY) * 0.5; + } else { + boundingBox->Y += (FrameHeight - MaxY) * 0.5; + } + break; + case StringAlignmentFar: + if (format->formatFlags & StringFormatFlagsDirectionVertical) { + boundingBox->X += (FrameHeight - MaxY); + } else { + boundingBox->Y += (FrameHeight - MaxY); + } + break; + } + + switch (AlignHorz) { + case StringAlignmentCenter: + if (format->formatFlags & StringFormatFlagsDirectionVertical) { + boundingBox->Y += (FrameWidth - MaxX) * 0.5; + } else { + boundingBox->X += (FrameWidth - MaxX) * 0.5; + } + break; + case StringAlignmentFar: + if (format->formatFlags & StringFormatFlagsDirectionVertical) { + boundingBox->Y += (FrameWidth - MaxX); + } else { + boundingBox->X += (FrameWidth - MaxX); + } + break; + } } if (codepointsFitted) { diff --git a/tests/testtext.c b/tests/testtext.c index 8023d8aa9..09d68b9f1 100644 --- a/tests/testtext.c +++ b/tests/testtext.c @@ -191,14 +191,12 @@ static void test_measure_string_alignment(void) rect.Width = 200.0; rect.Height = 100.0; set_rect_empty (&bounds); -#ifdef USE_PANGO_RENDERING status = GdipMeasureString (graphics, teststring1, 1, font, &rect, format, &bounds, NULL, NULL); expect (Ok, status); expectf_ (td[i].x_x0 + td[i].x_xx * bounds.Width + 5.0, bounds.X, 0.6); expectf_ (td[i].y_y0 + td[i].y_yy * bounds.Height + 10.0, bounds.Y, 0.6); expectf_ (td[i].right_x0 + td[i].right_xx * bounds.Width + 5.0, bounds.X + bounds.Width, 0.6); expectf_ (td[i].bottom_y0 + td[i].bottom_yy * bounds.Height + 10.0, bounds.Y + bounds.Height, 0.6); -#endif status = GdipMeasureCharacterRanges (graphics, teststring1, 1, font, &rect, format, 1, ®ion); expect (Ok, status);