http://bugs.winehq.org/show_bug.cgi?id=59068
Bug ID: 59068 Summary: ScriptPlace() total run width sum inconsistent with advance width array sum Product: Wine Version: 10.0 Hardware: x86-64 OS: Linux Status: UNCONFIRMED Severity: normal Priority: P2 Component: usp10 Assignee: wine-bugs@list.winehq.org Reporter: benc.marek.elektro98@proton.me Distribution: ---
Created attachment 79842 --> http://bugs.winehq.org/attachment.cgi?id=79842 Example program with ScriptPlace() that displays a run's advance width.
Hello,
I'm working on a Uniscribe text editor and doing a bit of testing with Wine too, and I've hit an issue where the ScriptPlace() function's last argument, the ABC width of the run, occasionally has an inconsistent width as compared to the sum of the advance width elements provided through the piAdvance array.
The MSDN documentation appears to suggest that the sum of the fields provided by the ABC parameter should match the total width of the run: https://learn.microsoft.com/en-us/windows/win32/api/usp10/nf-usp10-scriptpla...
""" The composite ABC width for the whole item identifies how much the glyphs overhang to the left of the start position and to the right of the length implied by the sum of the advance widths. The total advance width of the line is exactly abcA+abcB+abcC. The abcA and abcC values are maintained as proportions of the cell height represented in 8 bits and are thus roughly +/-1 percent. The total width retrieved, which is the sum of the abcA+abcB+abcC values indicated by piAdvance, is accurate to the resolution of the TrueType shaping engine. """
I'm adding the source code of a simple demonstration program to show the error in the attachment. Here's the build command:
$ i686-w64-mingw32-gcc -o scriptplace_abc_testcase.exe scriptplace_abc_testcase.c -lusp10 -lgdi32 -municode -O2 -Wall -Wextra -W
This example uses a string that triggered the condition for me in my text editing widget, which I noticed as the caret being misaligned, since the distance of the caret from the edge of the run was measured with the assumption that the total advance width is abcA+abcB+abcC, but that is not the case.
The example program generates a single text run, displays it on-screen with the default UI font retrieved by SPI_GETNONCLIENTMETRICS (Tahoma) with a 24 pt size, and displays the advance width value for each glyph, their sum, the ABC values, and their sum. On Debian 13 with wine-10.0 (Debian 10.0~repack-6), I'm getting the following:
testprog_load_1st_glyph_run(): first_run->abc: { abcA: 16, abcB: 178, abcC: 28 }, total: 222 testprog_load_1st_glyph_run(): first_run->glyph_advances: [ 17, 17, 15, 12, 10, 24, 8, 12, 18, 10, 12, 18, 12, 17, 17 }, total: 219
On Windows, I'm getting the following:
testprog_load_1st_glyph_run(): first_run->abc: { abcA: 0, abcB: 209, abcC: 0 }, total: 209 testprog_load_1st_glyph_run(): first_run->glyph_advances: [ 20, 17, 16, 9, 9, 23, 7, 9, 18, 9, 9, 18, 11, 17, 17 }, total: 209
I was able to trace down the error in the Wine source code. The advances and ABC are initially set to the same value in ScriptShape, see the following: https://gitlab.winehq.org/wine/wine/-/blob/master/dlls/gdi32/uniscribe/usp10...
""" if (pABC) { pABC->abcA += abc.abcA; pABC->abcB += abc.abcB; pABC->abcC += abc.abcC; } if (piAdvance) piAdvance[i] = abc.abcA + abc.abcB + abc.abcC; }
SHAPE_ApplyOpenTypePositions(hdc, (ScriptCache *)*psc, psa, pwGlyphs, cGlyphs, piAdvance, pGoffset);
"""
But if you look just below, there is a SHAPE_ApplyOpenTypePositions() call that accepts the glyph advance array as an argument. This eventually ends up calling GPOS_apply_lookup() in https://gitlab.winehq.org/wine/wine/-/blob/master/dlls/gdi32/uniscribe/opent... - which adjusts the advances:
""" if (advance.x || advance.y) { GPOS_convert_design_units_to_device(lpotm, lplogfont, advance.x, advance.y, &devX, &devY); piAdvance[glyph_index] += round(devX); if (advance.y) FIXME("Unhandled adjustment to Y advancement\n"); } break; """
Clearly, this causes the advance array and the ABC width to de-synchronize.
I also don't think the way the ABC width is calculated in the first place is correct, I see the As and Cs being summed up, but shouldn't they only be propagated for the left-most and right-most glyph, and otherwise be summed up in the B? But that's a separate issue, and it doesn't cause a problem for me.
Thanks, Marek