Skip to content
This repository was archived by the owner on Mar 7, 2025. It is now read-only.

Commit ca7e69d

Browse files
filipnavaramarek-safar
authored andcommitted
Initial test and fixes for GdipMeasureCharacterRanges (#275)
* Test GdipMeasureCharacterRanges output for alignment. * Fix Pango text measuring to pass the test. * Move Cairo text alignment code from DrawString to MeasureString and fix up MeasureCharacterRanges to return correct coordinates. Also simplify and fix hotkey underline drawing. * Apply alignment to bounding box in cairo_MeasureString.
1 parent 730bfdc commit ca7e69d

File tree

3 files changed

+120
-90
lines changed

3 files changed

+120
-90
lines changed

src/text-cairo.c

Lines changed: 88 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,40 @@ MeasureString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int *length
705705
boundingBox->Width = gdip_convgr_unitx (graphics, boundingBox->Width);
706706
boundingBox->Height = gdip_convgr_unity (graphics, boundingBox->Height);
707707
}
708+
709+
switch (AlignVert) {
710+
case StringAlignmentCenter:
711+
if (format->formatFlags & StringFormatFlagsDirectionVertical) {
712+
boundingBox->X += (FrameHeight - MaxY) * 0.5;
713+
} else {
714+
boundingBox->Y += (FrameHeight - MaxY) * 0.5;
715+
}
716+
break;
717+
case StringAlignmentFar:
718+
if (format->formatFlags & StringFormatFlagsDirectionVertical) {
719+
boundingBox->X += (FrameHeight - MaxY);
720+
} else {
721+
boundingBox->Y += (FrameHeight - MaxY);
722+
}
723+
break;
724+
}
725+
726+
switch (AlignHorz) {
727+
case StringAlignmentCenter:
728+
if (format->formatFlags & StringFormatFlagsDirectionVertical) {
729+
boundingBox->Y += (FrameWidth - MaxX) * 0.5;
730+
} else {
731+
boundingBox->X += (FrameWidth - MaxX) * 0.5;
732+
}
733+
break;
734+
case StringAlignmentFar:
735+
if (format->formatFlags & StringFormatFlagsDirectionVertical) {
736+
boundingBox->Y += (FrameWidth - MaxX);
737+
} else {
738+
boundingBox->X += (FrameWidth - MaxX);
739+
}
740+
break;
741+
}
708742
}
709743

710744
if (codepointsFitted) {
@@ -727,6 +761,44 @@ MeasureString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int *length
727761
*linesFilled = floor (height / LineHeight);
728762
}
729763

764+
if (AlignHorz != StringAlignmentNear || AlignVert != StringAlignmentNear) {
765+
// Update alignment
766+
int length = 0;
767+
int current_line_length = 0;
768+
for (i = 0; i < StringLen; i++) {
769+
if (i == current_line_length) {
770+
length = StringDetails[i].LineLen;
771+
current_line_length = min(length + i, StringLen);
772+
}
773+
774+
switch (AlignHorz) {
775+
case StringAlignmentNear:
776+
break;
777+
case StringAlignmentCenter:
778+
if ((current_line_length == 1) || (StringDetails [current_line_length - 1].PosX > 0)) {
779+
StringDetails[i].PosX += (FrameWidth - StringDetails [current_line_length - 1].PosX -
780+
StringDetails [current_line_length - 1].Width) / 2;
781+
}
782+
break;
783+
case StringAlignmentFar:
784+
StringDetails[i].PosX += FrameWidth - StringDetails [current_line_length - 1].PosX -
785+
StringDetails [current_line_length - 1].Width;
786+
break;
787+
}
788+
789+
switch (AlignVert) {
790+
case StringAlignmentNear:
791+
break;
792+
case StringAlignmentCenter:
793+
StringDetails[i].PosY += (FrameHeight - MaxY) / 2;
794+
break;
795+
case StringAlignmentFar:
796+
StringDetails[i].PosY += FrameHeight - MaxY;
797+
break;
798+
}
799+
}
800+
}
801+
730802
/* if asked, supply extra data to be reused when drawing the same string */
731803
if (data) {
732804
data->align_horz = AlignHorz;
@@ -813,55 +885,13 @@ DrawString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int length, GD
813885

814886
if ((fmt->formatFlags & StringFormatFlagsDirectionVertical)==0) {
815887
CursorX = rc->X + StringDetails[i].PosX;
816-
switch (AlignHorz) {
817-
case StringAlignmentNear:
818-
break;
819-
case StringAlignmentCenter:
820-
/* PosX isn't calculated if the char is out of the bounding box (#79573/#79685) */
821-
if ((current_line_length == 1) || (StringDetails [current_line_length-1].PosX > 0)) {
822-
CursorX += (rc->Width - StringDetails [current_line_length-1].PosX -
823-
StringDetails [current_line_length-1].Width) / 2;
824-
}
825-
/* which means that the line is too long so no centering is required */
826-
break;
827-
case StringAlignmentFar:
828-
CursorX += rc->Width - StringDetails [current_line_length-1].PosX -
829-
StringDetails [current_line_length-1].Width;
830-
break;
831-
}
832-
833-
switch (AlignVert) {
834-
case StringAlignmentNear: CursorY=rc->Y+StringDetails[i].PosY+LineHeight; break;
835-
case StringAlignmentCenter: CursorY=rc->Y+(rc->Height-MaxY)/2+StringDetails[i].PosY+LineHeight; break;
836-
case StringAlignmentFar: CursorY=rc->Y+rc->Height-MaxY+StringDetails[i].PosY+LineHeight; break;
837-
}
888+
CursorY = rc->Y + StringDetails[i].PosY + LineHeight;
838889

839890
gdip_cairo_move_to (graphics, CursorX, CursorY, FALSE, TRUE);
840891
cairo_show_text (graphics->ct, (const char *) String);
841892
} else {
842-
CursorY = rc->Y;
843-
switch (AlignHorz) {
844-
case StringAlignmentNear:
845-
break;
846-
case StringAlignmentCenter:
847-
/* PosX isn't calculated if the char is out of the bounding box (#79573/#79685) */
848-
if ((current_line_length == 1) || (StringDetails [current_line_length-1].PosX > 0)) {
849-
CursorY += (rc->Height - StringDetails[current_line_length-1].PosX -
850-
StringDetails [current_line_length-1].Width) / 2;
851-
}
852-
/* which means that the line is too long so no centering is required */
853-
break;
854-
case StringAlignmentFar:
855-
CursorY += rc->Height - StringDetails[current_line_length-1].PosX -
856-
StringDetails [current_line_length-1].Width;
857-
break;
858-
}
859-
860-
switch (AlignVert) {
861-
case StringAlignmentNear: CursorX=rc->X + StringDetails[i].PosX+StringDetails[i].PosY; break;
862-
case StringAlignmentCenter: CursorX=rc->X + StringDetails[i].PosX+(rc->Width-MaxY)/2+StringDetails[i].PosY; break;
863-
case StringAlignmentFar: CursorX=rc->X + StringDetails[i].PosX+rc->Width-MaxY+StringDetails[i].PosY; break;
864-
}
893+
CursorY = rc->Y + StringDetails[i].PosX;
894+
CursorX = rc->X + StringDetails[i].PosY;
865895

866896
/* Rotate text for vertical drawing */
867897
cairo_save (graphics->ct);
@@ -915,48 +945,23 @@ DrawString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int length, GD
915945
if (fmt->hotkeyPrefix==HotkeyPrefixShow && data->has_hotkeys) {
916946
GpStringDetailStruct *CurrentDetail = StringDetails;
917947
for (i=0; i<StringLen; i++) {
918-
if (CurrentDetail->Flags & STRING_DETAIL_LINESTART) {
948+
if (CurrentDetail->Flags & STRING_DETAIL_HOTKEY) {
919949
if ((fmt->formatFlags & StringFormatFlagsDirectionVertical)==0) {
920-
switch (AlignHorz) {
921-
case StringAlignmentNear: CursorX=rc->X; break;
922-
case StringAlignmentCenter: CursorX=rc->X+(rc->Width-StringDetails[i+StringDetails[i].LineLen-1].PosX-StringDetails[i+StringDetails[i].LineLen-1].Width)/2; break;
923-
case StringAlignmentFar: CursorX=rc->X+rc->Width-StringDetails[i+StringDetails[i].LineLen-1].PosX-StringDetails[i+StringDetails[i].LineLen-1].Width; break;
924-
}
925-
926-
switch (AlignVert) {
927-
case StringAlignmentNear: CursorY=rc->Y+StringDetails[i].PosY+LineHeight; break;
928-
case StringAlignmentCenter: CursorY=rc->Y+(rc->Height-MaxY)/2+StringDetails[i].PosY+LineHeight; break;
929-
case StringAlignmentFar: CursorY=rc->Y+rc->Height-MaxY+StringDetails[i].PosY+LineHeight; break;
930-
}
950+
CursorX = rc->X + StringDetails[i].PosX;
951+
CursorY = rc->Y + StringDetails[i].PosY + LineHeight;
931952
} else {
932-
switch (AlignHorz) {
933-
case StringAlignmentNear: CursorY=rc->Y; break;
934-
case StringAlignmentCenter: CursorY=rc->Y+(rc->Height-StringDetails[i+StringDetails[i].LineLen-1].PosX-StringDetails[i+StringDetails[i].LineLen-1].Width)/2; break;
935-
case StringAlignmentFar: CursorY=rc->Y+rc->Height-StringDetails[i+StringDetails[i].LineLen-1].PosX-StringDetails[i+StringDetails[i].LineLen-1].Width; break;
936-
}
937-
938-
switch (AlignVert) {
939-
case StringAlignmentNear: CursorX=rc->X+StringDetails[i].PosY; break;
940-
case StringAlignmentCenter: CursorX=rc->X+(rc->Width-MaxY)/2+StringDetails[i].PosY; break;
941-
case StringAlignmentFar: CursorX=rc->X+rc->Width-MaxY+StringDetails[i].PosY; break;
942-
}
953+
CursorY = rc->Y + StringDetails[i].PosX;
954+
CursorX = rc->X + StringDetails[i].PosY;
943955
}
944-
}
945956

946-
if (CurrentDetail->Flags & STRING_DETAIL_HOTKEY) {
947957
if ((fmt->formatFlags & StringFormatFlagsDirectionVertical)==0) {
948-
CursorX+=CurrentDetail->PosX;
949958
cairo_set_line_width(graphics->ct, 1);
950-
951-
gdip_cairo_move_to (graphics, (int)(CursorX), (int)(CursorY+FontExtent.descent/2), FALSE, TRUE);
952-
gdip_cairo_line_to (graphics, (int)(CursorX+CurrentDetail->Width), (int)(CursorY+FontExtent.descent/2), FALSE, TRUE);
959+
gdip_cairo_move_to (graphics, (int)(CursorX), (int)(CursorY+FontExtent.descent), FALSE, TRUE);
960+
gdip_cairo_line_to (graphics, (int)(CursorX+CurrentDetail->Width), (int)(CursorY+FontExtent.descent), FALSE, TRUE);
953961
cairo_stroke (graphics->ct);
954-
CursorX-=CurrentDetail->PosX;
955962
} else {
956-
CursorY+=CurrentDetail->PosX;
957-
gdip_cairo_move_to (graphics, (int)(CursorX-FontExtent.descent/2), (int)(CursorY), FALSE, TRUE);
958-
gdip_cairo_line_to (graphics, (int)(CursorX-FontExtent.descent/2), (int)(CursorY+CurrentDetail->Width), FALSE, TRUE);
959-
CursorY-=CurrentDetail->PosX;
963+
gdip_cairo_move_to (graphics, (int)(CursorX-FontExtent.descent), (int)(CursorY), FALSE, TRUE);
964+
gdip_cairo_line_to (graphics, (int)(CursorX-FontExtent.descent), (int)(CursorY+CurrentDetail->Width), FALSE, TRUE);
960965
}
961966
}
962967
CurrentDetail++;
@@ -1164,13 +1169,13 @@ cairo_MeasureCharacterRanges (GpGraphics *graphics, GDIPCONST WCHAR *stringUnico
11641169
continue;
11651170

11661171
if (format->formatFlags & StringFormatFlagsDirectionVertical) {
1167-
charRect.X = StringDetails [j].PosY;
1168-
charRect.Y = StringDetails [j].PosX;
1172+
charRect.X = layoutRect->X + StringDetails [j].PosY;
1173+
charRect.Y = layoutRect->Y + StringDetails [j].PosX;
11691174
charRect.Width = lineHeight;
11701175
charRect.Height = StringDetails [j].Width;
11711176
} else {
1172-
charRect.X = StringDetails [j].PosX;
1173-
charRect.Y = StringDetails [j].PosY;
1177+
charRect.X = layoutRect->X + StringDetails [j].PosX;
1178+
charRect.Y = layoutRect->Y + StringDetails [j].PosY;
11741179
charRect.Width = StringDetails [j].Width;
11751180
charRect.Height = lineHeight;
11761181
}

src/text-pango.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -714,10 +714,17 @@ pango_MeasureCharacterRanges (GpGraphics *graphics, GDIPCONST WCHAR *stringUnico
714714
GpRectF charRect;
715715

716716
pango_layout_index_to_pos (layout, idxUtf8, &box);
717-
charRect.X = (float)box.x / PANGO_SCALE + box_offset.X;
718-
charRect.Y = (float)box.y / PANGO_SCALE + box_offset.Y;
719-
charRect.Width = (float)box.width / PANGO_SCALE;
720-
charRect.Height = (float)box.height / PANGO_SCALE;
717+
if (format->formatFlags & StringFormatFlagsDirectionVertical) {
718+
charRect.X = (float)box.y / PANGO_SCALE;
719+
charRect.Y = (float)box.x / PANGO_SCALE;
720+
charRect.Width = (float)box.height / PANGO_SCALE;
721+
charRect.Height = (float)box.width / PANGO_SCALE;
722+
} else {
723+
charRect.X = (float)box.x / PANGO_SCALE;
724+
charRect.Y = (float)box.y / PANGO_SCALE;
725+
charRect.Width = (float)box.width / PANGO_SCALE;
726+
charRect.Height = (float)box.height / PANGO_SCALE;
727+
}
721728
/* Normalize values (width/height can be negative) */
722729
if (charRect.Width < 0) {
723730
charRect.Width = -charRect.Width;
@@ -727,6 +734,8 @@ pango_MeasureCharacterRanges (GpGraphics *graphics, GDIPCONST WCHAR *stringUnico
727734
charRect.Height = -charRect.Height;
728735
charRect.Y -= charRect.Height;
729736
}
737+
charRect.X += box_offset.X + layoutRect->X;
738+
charRect.Y += box_offset.Y + layoutRect->Y;
730739
// 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);
731740
status = GdipCombineRegionRect (regions [i], &charRect, CombineModeUnion);
732741
if (status != Ok)

tests/testtext.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ static void test_measure_string(void)
108108
GdipDisposeImage (image);
109109
}
110110

111+
#endif
112+
111113
static void test_measure_string_alignment(void)
112114
{
113115
GpStringFormat *format;
@@ -117,8 +119,10 @@ static void test_measure_string_alignment(void)
117119
GpFont *font;
118120
GpStatus status;
119121
GpRectF rect, bounds;
122+
GpRegion *region;
120123
const WCHAR teststring1[] = { 'M', 0 };
121124
INT i;
125+
static const CharacterRange character_range = { 0, 1 };
122126
static const struct test_data
123127
{
124128
INT flags;
@@ -167,12 +171,16 @@ static void test_measure_string_alignment(void)
167171
expect (Ok, status);
168172
status = GdipCreateFont (family, 10, FontStyleRegular, UnitPixel, &font);
169173
expect (Ok, status);
174+
status = GdipCreateRegion (&region);
175+
expect (Ok, status);
170176
status = GdipCreateBitmapFromScan0 (400, 400, 0, PixelFormat32bppRGB, NULL, (GpBitmap **) &image);
171177
expect (Ok, status);
172178
status = GdipGetImageGraphicsContext (image, &graphics);
173179
expect (Ok, status);
174180
ok (graphics != NULL, "Expected graphics to be initialized\n");
175181

182+
GdipSetStringFormatMeasurableCharacterRanges (format, 1, &character_range);
183+
176184
for (i = 0; i < sizeof(td) / sizeof(td[0]); i++) {
177185
GdipSetStringFormatFlags (format, td[i].flags);
178186
GdipSetStringFormatAlign (format, td[i].alignment);
@@ -189,26 +197,34 @@ static void test_measure_string_alignment(void)
189197
expectf_ (td[i].y_y0 + td[i].y_yy * bounds.Height + 10.0, bounds.Y, 0.6);
190198
expectf_ (td[i].right_x0 + td[i].right_xx * bounds.Width + 5.0, bounds.X + bounds.Width, 0.6);
191199
expectf_ (td[i].bottom_y0 + td[i].bottom_yy * bounds.Height + 10.0, bounds.Y + bounds.Height, 0.6);
200+
201+
status = GdipMeasureCharacterRanges (graphics, teststring1, 1, font, &rect, format, 1, &region);
202+
expect (Ok, status);
203+
status = GdipGetRegionBounds (region, graphics, &bounds);
204+
expect (Ok, status);
205+
expectf_ (td[i].x_x0 + td[i].x_xx * bounds.Width + 5.0, bounds.X, 3.0);
206+
expectf_ (td[i].y_y0 + td[i].y_yy * bounds.Height + 10.0, bounds.Y, 3.0);
207+
expectf_ (td[i].right_x0 + td[i].right_xx * bounds.Width + 5.0, bounds.X + bounds.Width, 3.0);
208+
expectf_ (td[i].bottom_y0 + td[i].bottom_yy * bounds.Height + 10.0, bounds.Y + bounds.Height, 3.0);
192209
}
193210

194211
GdipDeleteGraphics (graphics);
195212
GdipDeleteFont (font);
196213
GdipDeleteFontFamily (family);
197214
GdipDeleteStringFormat (format);
198215
GdipDisposeImage (image);
216+
GdipDeleteRegion (region);
199217
}
200218

201-
#endif
202-
203219
int
204220
main (int argc, char**argv)
205221
{
206222
STARTUP;
207223

208224
#if defined(USE_PANGO_RENDERING) || defined(USE_WINDOWS_GDIPLUS)
209225
test_measure_string ();
210-
test_measure_string_alignment ();
211226
#endif
227+
test_measure_string_alignment ();
212228

213229
SHUTDOWN;
214230
return 0;

0 commit comments

Comments
 (0)