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
