Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix text baseline position for some fonts #669

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/graphics-path.c
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,7 @@ GdipAddPathString (GpPath *path, GDIPCONST WCHAR *string, int length,
{
GpRectF box;
GpPointF box_offset;
float baseline_offset;
PangoLayout* layout;
GpStringFormat *string_format;

Expand All @@ -1296,8 +1297,8 @@ GdipAddPathString (GpPath *path, GDIPCONST WCHAR *string, int length,
return status;
}

layout = gdip_pango_setup_layout (cr, string, length, font, layoutRect, &box, &box_offset, string_format, NULL);
cairo_move_to (cr, layoutRect->X + box_offset.X, layoutRect->Y + box_offset.Y);
layout = gdip_pango_setup_layout (cr, string, length, font, layoutRect, &box, &box_offset, &baseline_offset, string_format, NULL);
cairo_move_to (cr, layoutRect->X + box_offset.X, layoutRect->Y + box_offset.Y + baseline_offset);
pango_cairo_layout_path (cr, layout);
g_object_unref (layout);

Expand Down
2 changes: 1 addition & 1 deletion src/text-pango-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@


PangoLayout* gdip_pango_setup_layout (cairo_t *cr, GDIPCONST WCHAR *stringUnicode, INT length, GDIPCONST GpFont *font,
GDIPCONST RectF *rc, RectF *box, PointF *box_offset, GDIPCONST GpStringFormat *format, INT **charsRemoved);
GDIPCONST RectF *rc, RectF *box, PointF *box_offset, float *baseline_offset, 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;
Expand Down
31 changes: 26 additions & 5 deletions src/text-pango.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ gdip_process_string (gchar *text, int length, int removeAccelerators, int trimSp

PangoLayout*
gdip_pango_setup_layout (cairo_t *cr, GDIPCONST WCHAR *stringUnicode, int length, GDIPCONST GpFont *font,
GDIPCONST RectF *rc, RectF *box, PointF *box_offset, GDIPCONST GpStringFormat *format, int **charsRemoved)
GDIPCONST RectF *rc, RectF *box, PointF *box_offset, float *baseline_offset, GDIPCONST GpStringFormat *format, int **charsRemoved)
{
GpStringFormat *fmt;
PangoLayout *layout;
Expand Down Expand Up @@ -468,9 +468,25 @@ gdip_pango_setup_layout (cairo_t *cr, GDIPCONST WCHAR *stringUnicode, int length

box_offset->X = 0;
box_offset->Y = 0;
*baseline_offset = 0;

switch (fmt->lineAlignment) {
case StringAlignmentNear:
if (!(fmt->formatFlags & StringFormatFlagsDirectionVertical)) {
// Some fonts have interesting metrics, and cause Pango to position them differently to Windows.
// For instance, Calibri and the free metric-equivalent Carlito. It is positioned higher, because Pango
// uses a different ascent value than Windows. We can handle that by drawing it slightly lower.
USHORT ascent;
USHORT em_size;
if (GdipGetCellAscent(font->family, (INT) font->style, &ascent) == Ok &&
GdipGetEmHeight(font->family, (INT) font->style, &em_size) == Ok) {

int baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;
int correct_baseline = (int) ceil(font->sizeInPixels / em_size * ascent);
if (baseline < correct_baseline)
*baseline_offset = correct_baseline - baseline;
}
}
break;
case StringAlignmentCenter:
box_offset->Y += (FrameHeight - box->Height) * 0.5;
Expand Down Expand Up @@ -528,6 +544,7 @@ pango_DrawString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, INT leng
PangoLayout *layout;
RectF box;
PointF box_offset;
float baseline_offset;

/* Setup cairo */
if (brush) {
Expand All @@ -538,7 +555,7 @@ pango_DrawString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, INT leng

cairo_save (graphics->ct);

layout = gdip_pango_setup_layout (graphics->ct, stringUnicode, length, font, rc, &box, &box_offset, format, NULL);
layout = gdip_pango_setup_layout (graphics->ct, stringUnicode, length, font, rc, &box, &box_offset, &baseline_offset, format, NULL);
if (!layout) {
cairo_restore (graphics->ct);
return OutOfMemory;
Expand All @@ -552,7 +569,7 @@ pango_DrawString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, INT leng
cairo_clip (graphics->ct);
}

gdip_cairo_move_to (graphics, rc->X + box_offset.X, rc->Y + box_offset.Y, FALSE, TRUE);
gdip_cairo_move_to (graphics, rc->X + box_offset.X, rc->Y + box_offset.Y + baseline_offset, FALSE, TRUE);
pango_cairo_show_layout (graphics->ct, layout);

g_object_unref (layout);
Expand All @@ -569,15 +586,17 @@ pango_MeasureString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, INT l
PangoRectangle logical;
PangoLayoutIter *iter;
PointF box_offset;
float baseline_offset;
int *charsRemoved = NULL;

cairo_save (graphics->ct);

layout = gdip_pango_setup_layout (graphics->ct, stringUnicode, length, font, rc, boundingBox, &box_offset, format, &charsRemoved);
layout = gdip_pango_setup_layout (graphics->ct, stringUnicode, length, font, rc, boundingBox, &box_offset, &baseline_offset, format, &charsRemoved);
if (!layout) {
cairo_restore (graphics->ct);
return OutOfMemory;
}
boundingBox->Height += baseline_offset;

if (codepointsFitted || linesFilled) {
int charsFitted;
Expand Down Expand Up @@ -678,6 +697,7 @@ pango_MeasureCharacterRanges (GpGraphics *graphics, GDIPCONST WCHAR *stringUnico
int i, j;
GpRectF boundingBox;
GpPointF box_offset;
float baseline_offset;

if (layoutRect->Width <= 0.0 && layoutRect->Height < 0.0) {
/* special case only if BOTH values are negative */
Expand All @@ -688,7 +708,7 @@ pango_MeasureCharacterRanges (GpGraphics *graphics, GDIPCONST WCHAR *stringUnico

cairo_save (graphics->ct);

layout = gdip_pango_setup_layout (graphics->ct, stringUnicode, length, font, layoutRect, &boundingBox, &box_offset, format, NULL);
layout = gdip_pango_setup_layout (graphics->ct, stringUnicode, length, font, layoutRect, &boundingBox, &box_offset, &baseline_offset, format, NULL);
if (!layout) {
cairo_restore (graphics->ct);
return OutOfMemory;
Expand Down Expand Up @@ -750,6 +770,7 @@ pango_MeasureCharacterRanges (GpGraphics *graphics, GDIPCONST WCHAR *stringUnico
}
charRect.X += box_offset.X + layoutRect->X;
charRect.Y += box_offset.Y + layoutRect->Y;
charRect.Height += baseline_offset;
// 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)
Expand Down
34 changes: 34 additions & 0 deletions tests/testtext.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ static void test_measure_string(void)
int glyphs;
int lines;
const SHORT fontSize = 10;
GpFontCollection *collection;
WCHAR *ttfFile;
INT count;
GpFontFamily *families[1];
GpFont *font2;
REAL fontHeight;

status = GdipCreateStringFormat (0, 0, &format);
expect (Ok, status);
Expand Down Expand Up @@ -179,6 +185,34 @@ static void test_measure_string(void)
expect (1, lines);
expectf ((double)fontSize, bounds.Width); // An em-space should be the same width as the font size.

// Check measuring a string drawn with a font that has metrics which cause Pango to draw differently to GDI+
GdipNewPrivateFontCollection (&collection);
ttfFile = createWchar ("test.ttf");
status = GdipPrivateAddFontFile (collection, ttfFile);
expect (Ok, status);
status = GdipGetFontCollectionFamilyList (collection, 2, families, &count);
expect (Ok, status);
expect (1, count);
status = GdipCreateFont (families[0], 36, 0, UnitPixel, &font2);
expect (Ok, status);
status = GdipGetFontHeight (font2, graphics, &fontHeight);
expect (Ok, status);
set_rect_empty (&bounds);
status = GdipMeasureString (graphics, teststring1, 1, font2, &rect, format, &bounds, &glyphs, &lines);
expect (Ok, status);
expectf (5.0, bounds.X);
expectf (5.0, bounds.Y);
expectf_ (20.0, bounds.Width, 3);
expectf_ (fontHeight, bounds.Height, 3);
expect (1, glyphs);
expect (1, lines);
GdipDeleteFont(font2);
// This causes a crash in GDI+.
#if !defined(USE_WINDOWS_GDIPLUS)
GdipDeleteFontFamily (families[0]);
#endif
GdipDeletePrivateFontCollection (&collection);

// MonoTests.System.Drawing.GraphicsTest.MeasureString_Wrapping_Dots
GdipDeleteStringFormat (format);
status = GdipCreateStringFormat (0, 0, &format);
Expand Down