When rendering Thai text, scaling fonts via `deriveFont(float)` seems to always 
work correctly. However, when the font is scaled via 
`deriveFont(AffineTransform)`, the vowels above the main line of text are 
misplaced, mangling the meaning of the text.

This appears to be caused by GPOS adjustments performed by HarfBuzz which are 
calculated at the wrong scale if the full (point size + AffineTransform) scale 
is not provided to HarfBuzz upon font creation.

The fix must change both the FFM and JNI HarfBuzz integration code, so it looks 
a bit longer than it actually is. The FFM and JNI changes were made in separate 
commits, so it might be helpful to review only the FFM changes in a first pass, 
and then have a look at the corresponding JNI changes (which are in a separate 
commit).

Some observations:

- jdk_font_create_hbp() needs to incorporate font transform scaling, not just 
the font point size scaling
- jdk_font_create_hbp() needs two independent font scales / point sizes (x and 
y)
- jdk_font_create_hbp() applies devTx, only for it to be removed in 
store_layout_results(); we might as well leave devTx out from the start

Sequence of changes made to the FFM code:

- Replace SDCache.gtx (glyph transform = font transform without translation, 
plus point size scaling, plus device transform) with SDCache.ftx (font 
transform including translation, plus point size scaling)
- Remove SDCache.delta (just keep translation components in new ftx field 
instead)
- Remove SDCache.dtx (never used)
- Remove devScale from store_layout_results() parameters (dev scale no longer 
included during layout, so doesn't need to be undone here)
- Remove destroy function param from jdk_font_create_hbp() (never used, always 
NULL)
- Calculate x and y font point sizes in jdk_hb_shape (based on full font 
transform, including font pt size), instead of using only font pt size
- Pass these x and y point sizes into jdk_font_create_hbp()
- Remove now-unused ptSize parameter from jdk_font_create_hbp()
- Remove now-unused devScale parameter from jdk_font_create_hbp()
- Remove now-unused ptSize parameter from jdk_hb_shape()
- Remove now-unused ptSize parameter from HBShaper.shape()

Sequence of changes made to the JNI code:

- Remove destroy function from _hb_jdk_font_create(), not used just like in FFM 
code
- Use xPtSize and yPtSize in _hb_jdk_font_create(), instead of ptSize and 
devScale
- Remove devScale from storeGVData (not used during layout, so doesn't need to 
be undone at the end)
- Remove now-unused ptSize and devScale from JDKFontInfo
- Remove now-unused ptSize from createJDKFontInfo()
- Remove now-unused ptSize from Java_sun_font_SunLayoutEngine_shape()
- Remove now-unused ptSize from SunLayoutEngine.layout()
- Remove now-unused ptSize from GlyphLayout class

The following tests pass on Linux, Windows and macOS:

- `make test TEST="jtreg:test/jdk/java/awt/font"`
- `make test TEST="jtreg:test/jdk/java/awt/Graphics2D/DrawString"`
- `make test TEST="jtreg:test/jdk/java/awt/Graphics2D/DrawString/GposTest.java" 
JTREG="JAVA_OPTIONS=-Dsun.font.layout.ffm=true"`
- `make test TEST="jtreg:test/jdk/java/awt/Graphics2D/DrawString/GposTest.java" 
JTREG="JAVA_OPTIONS=-Dsun.font.layout.ffm=false"`

The new test `GposTest` includes an embedded font created just for this test, 
using the FontForge Python bindings (code included in the test class comments) 
which exercises the GPOS table in various ways. See the JavaDoc in `GposTest` 
for more details.

I also manually tested the text "\u0e25\u0e37\u0e25\u0e37\u0e25\u0e37" via 
Font2DTest (`build/linux-x86_64-server-release/jdk/bin/java 
src/demo/share/jfc/Font2DTest/Font2DTest.java`) with the Noto Sans Thai font, 
drawString, Plain style, size 60, with Font Transform Scale enabled, and 
verified that the text is laid out incorrectly before the fix and is laid out 
correctly after the fix.

I think the big open question is whether this causes a regression for 
JDK-8145901, which touched a lot of this code back in 2016. Unfortunately the 
bug report is private and I don't have access to a reproducer, so I can't be 
sure.

---------
- [x] I confirm that I make this contribution in accordance with the [OpenJDK 
Interim AI Policy](https://openjdk.org/legal/ai).

-------------

Commit messages:
 - Update copyright years
 - GposTest: fix rounding issue on Windows, improve test failure messages
 - Fix JNI code
 - Fix FFM code
 - Add test

Changes: https://git.openjdk.org/jdk/pull/30953/files
  Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=30953&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8269888
  Stats: 531 lines in 10 files changed: 437 ins; 45 del; 49 mod
  Patch: https://git.openjdk.org/jdk/pull/30953.diff
  Fetch: git fetch https://git.openjdk.org/jdk.git pull/30953/head:pull/30953

PR: https://git.openjdk.org/jdk/pull/30953

Reply via email to