From 1e6c4904a0a4bb6bc2fd2e6ac7e267886492c599 Mon Sep 17 00:00:00 2001 From: Karl Date: Fri, 24 Nov 2017 14:13:54 +0100 Subject: [PATCH 1/3] Corrections to MeasureCharacterRanges, improvements to DrawString / MeasureString MeasureCharacterRanges now counts LF, CR, tab as characters for measuring a CharacterRanges. Also utilises layoutRect. MeasureString now deals with all alignment, rather than DrawString. Doing so has meant that StringAlignment can now work correctly. Also handles boundingBox better. DrawString has been changed to work with the changes to MeasureString. --- src/text-cairo.c | 216 ++++++++++++++++++++++++++++------------------- 1 file changed, 129 insertions(+), 87 deletions(-) diff --git a/src/text-cairo.c b/src/text-cairo.c index 3ef448751..f1a1f4aac 100644 --- a/src/text-cairo.c +++ b/src/text-cairo.c @@ -707,11 +707,27 @@ MeasureString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int *length boundingBox->Width = MaxX; boundingBox->Height = MaxY; } - if ((rc->Width > 0) && (boundingBox->Width > rc->Width)) { - boundingBox->Width = rc->Width; + if (rc->Width > 0) { + if (boundingBox->Width > rc->Width) { + boundingBox->Width = rc->Width; + } else { + if (format->formatFlags & StringFormatFlagsDirectionVertical) { + boundingBox->Y += (rc->Width - boundingBox->Width) / 2; + } else { + boundingBox->X += (rc->Width - boundingBox->Width) / 2; + } + } } - if ((rc->Height > 0) && (boundingBox->Height > rc->Height)) { - boundingBox->Height = rc->Height; + if (rc->Height > 0) { + if (boundingBox->Height > rc->Height) { + boundingBox->Height = rc->Height; + } else { + if (format->formatFlags & StringFormatFlagsDirectionVertical) { + boundingBox->X += (rc->Height - boundingBox->Height) / 2; + } else { + boundingBox->Y += (rc->Height - boundingBox->Height) / 2; + } + } } /* avoid conversion computations if possible */ @@ -742,6 +758,72 @@ MeasureString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int *length double height = (boundingBox) ? boundingBox->Height : min (MaxY, rc->Height); *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); + } + + if ((format->formatFlags & StringFormatFlagsDirectionVertical) == 0) { + switch (AlignHorz) { + case StringAlignmentNear: + break; + case StringAlignmentCenter: + if ((current_line_length == 1) || (StringDetails [current_line_length - 1].PosX > 0)) { + StringDetails[i].PosX += (rc->Width - StringDetails [current_line_length - 1].PosX - + StringDetails [current_line_length - 1].Width) / 2; + } + break; + case StringAlignmentFar: + StringDetails[i].PosX += rc->Width - StringDetails [current_line_length - 1].PosX - + StringDetails [current_line_length - 1].Width; + break; + } + + switch (AlignVert) { + case StringAlignmentNear: + break; + case StringAlignmentCenter: + StringDetails[i].PosY += (rc->Height - MaxY) / 2; + break; + case StringAlignmentFar: + StringDetails[i].PosY += rc->Height - MaxY; + break; + } + } else { + switch (AlignVert) { + case StringAlignmentNear: + break; + case StringAlignmentCenter: + if ((current_line_length == 1) || (StringDetails [current_line_length - 1].PosX > 0)) { + StringDetails[i].PosX += (rc->Height - StringDetails [current_line_length - 1].PosX - + StringDetails [current_line_length - 1].Width) / 2; + } + break; + case StringAlignmentFar: + StringDetails[i].PosX += rc->Height - StringDetails [current_line_length - 1].PosX - + StringDetails [current_line_length - 1].Width; + break; + } + + switch (AlignHorz) { + case StringAlignmentNear: + break; + case StringAlignmentCenter: + StringDetails[i].PosY += (rc->Width - MaxY) / 2; + break; + case StringAlignmentFar: + StringDetails[i].PosY += rc->Width - MaxY; + break; + } + } + } + } /* if asked, supply extra data to be reused when drawing the same string */ if (data) { @@ -829,55 +911,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); @@ -933,29 +973,11 @@ DrawString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int length, GD for (i=0; iFlags & STRING_DETAIL_LINESTART) { 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; + 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; + CursorX=rc->X+StringDetails[i].PosY; } } @@ -1099,6 +1121,7 @@ cairo_MeasureCharacterRanges (GpGraphics *graphics, GDIPCONST WCHAR *stringUnico RectF charRect; RectF rc_coords, *layoutRect = &rc_coords; BOOL optimize_convert; + int index_matching[length]; WCHAR *CleanString; GpDrawTextData data; /* avoid recomputation of stuff done while measuring */ int StringLen = length; @@ -1141,7 +1164,34 @@ cairo_MeasureCharacterRanges (GpGraphics *graphics, GDIPCONST WCHAR *stringUnico if (status != Ok) goto cleanup; - lineHeight = data.line_height + data.descent; + lineHeight = data.line_height; //+ data.descent; + + j = 0; // index in StringDetails + for (i = 0; i < length; i++) { + switch (stringUnicode[i]) { + case '\r': /* CR */ + case '\t': /* Tab */ + case '\n': /* LF */ + { + index_matching[i] = -1; // not present in StringDetails + break; + } + + case '&': + { + if (format->hotkeyPrefix != HotkeyPrefixNone && (StringDetails[j].Flags & STRING_DETAIL_HOTKEY)) { + // Not present, because it is a hotkey + index_matching[i] = -1; + break; + } // else fall through to default + } + + default: + { + index_matching[i] = j++; + } + } + } /* Create a region for every char range */ for (i = 0; i < format->charRangeCount; i++) { @@ -1168,26 +1218,18 @@ cairo_MeasureCharacterRanges (GpGraphics *graphics, GDIPCONST WCHAR *stringUnico /* calculate the regions */ for (j = start; j < end; j++) { - - /* the prefix char (&) always count - even if we are not actually print it as a char */ - if ((StringDetails[j].Flags & STRING_DETAIL_HOTKEY) && (format->hotkeyPrefix != HotkeyPrefixNone)) { - end--; /* '&' count as invisible char */ - continue; - } - - /* workaround the fact that current implementation thinks LF is on the next line */ - if ((j == end - 1) && (StringDetails[j].Flags & STRING_DETAIL_LF)) + if (index_matching[j] == -1) continue; if (format->formatFlags & StringFormatFlagsDirectionVertical) { - charRect.X = StringDetails [j].PosY; - charRect.Y = StringDetails [j].PosX; + charRect.X = layoutRect->X + StringDetails [index_matching[j]].PosY; + charRect.Y = layoutRect->Y + StringDetails [index_matching[j]].PosX + data.descent; charRect.Width = lineHeight; - charRect.Height = StringDetails [j].Width; + charRect.Height = StringDetails [index_matching[j]].Width; } else { - charRect.X = StringDetails [j].PosX; - charRect.Y = StringDetails [j].PosY; - charRect.Width = StringDetails [j].Width; + charRect.X = layoutRect->X + StringDetails [index_matching[j]].PosX; + charRect.Y = layoutRect->Y + StringDetails [index_matching[j]].PosY + data.descent; + charRect.Width = StringDetails [index_matching[j]].Width; charRect.Height = lineHeight; } From 5ad77c26976c88c5440fbc66791bdd53c20c9149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Fri, 24 Nov 2017 15:36:09 +0100 Subject: [PATCH 2/3] Allocate index_matching array with malloc Using array initializer with a variable is a gnu extensions and doesn't work on MSVC. --- src/text-cairo.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/text-cairo.c b/src/text-cairo.c index f1a1f4aac..93135d1cf 100644 --- a/src/text-cairo.c +++ b/src/text-cairo.c @@ -1121,11 +1121,15 @@ cairo_MeasureCharacterRanges (GpGraphics *graphics, GDIPCONST WCHAR *stringUnico RectF charRect; RectF rc_coords, *layoutRect = &rc_coords; BOOL optimize_convert; - int index_matching[length]; + int *index_matching; WCHAR *CleanString; GpDrawTextData data; /* avoid recomputation of stuff done while measuring */ int StringLen = length; + index_matching = (int *) malloc (sizeof (int) * length); + if (!index_matching) + return OutOfMemory; + GpStatus status = AllocStringData (&CleanString, &StringDetails, length); if (status != Ok) return status; From b0881d5eedf07f8478d80e206e2f0bdb9c889a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Fri, 24 Nov 2017 15:40:12 +0100 Subject: [PATCH 3/3] Free index_matching --- src/text-cairo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/text-cairo.c b/src/text-cairo.c index 93135d1cf..8e79a45c7 100644 --- a/src/text-cairo.c +++ b/src/text-cairo.c @@ -1254,6 +1254,7 @@ cairo_MeasureCharacterRanges (GpGraphics *graphics, GDIPCONST WCHAR *stringUnico cleanup: /* Cleanup */ + free (index_matching); GdipFree (CleanString); GdipFree (StringDetails);