Skip to content

Conversation

@gredler
Copy link
Contributor

@gredler gredler commented Feb 17, 2025

On other platforms like Windows and Linux, the \n, \r and \t characters are ignored when drawing text to a Graphics2D object. On macOS this is not currently the case.

See, for example, CMap.getControlCodeGlyph(int, boolean) or RasterPrinterJob.removeControlChars(String).

This bug was found while running test/jdk/java/awt/print/PrinterJob/PrintTextTest.java on macOS.

The new test class passes on Linux, Windows and macOS.


Progress

  • Change must be properly reviewed (1 review required, with at least 1 Reviewer)
  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue

Issues

  • JDK-8350203: [macos] Newlines and tabs are not ignored when drawing text to a Graphics2D object (Bug - P4)
  • JDK-8353187: Test TextLayout/TestControls fails on macOS: width of 0x9, 0xa, 0xd isn't zero (Bug - P4)

Reviewers

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/23665/head:pull/23665
$ git checkout pull/23665

Update a local copy of the PR:
$ git checkout pull/23665
$ git pull https://git.openjdk.org/jdk.git pull/23665/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 23665

View PR using the GUI difftool:
$ git pr show -t 23665

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/23665.diff

Using Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented Feb 17, 2025

👋 Welcome back dgredler! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Feb 17, 2025

@gredler This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

8350203: [macos] Newlines and tabs are not ignored when drawing text to a Graphics2D object
8353187: Test TextLayout/TestControls fails on macOS: width of 0x9, 0xa, 0xd isn't zero

Reviewed-by: honkar, aivanov, prr

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been 79 new commits pushed to the master branch:

As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.

As you do not have Committer status in this project an existing Committer must agree to sponsor your change. Possible candidates are the reviewers of this PR (@honkar-jdk, @prrace, @aivanov-jdk) but any other Committer may sponsor as well.

➡️ To flag this PR as ready for integration with the above commit message, type /integrate in a new comment. (Afterwards, your sponsor types /sponsor in a new comment to perform the integration).

@openjdk openjdk bot added the rfr Pull request is ready for review label Feb 17, 2025
@openjdk
Copy link

openjdk bot commented Feb 17, 2025

@gredler The following label will be automatically applied to this pull request:

  • client

When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command.

@openjdk openjdk bot added the client client-libs-dev@openjdk.org label Feb 17, 2025
@mlbridge
Copy link

mlbridge bot commented Feb 17, 2025

Webrevs

@gredler
Copy link
Contributor Author

gredler commented Feb 26, 2025

If anyone has a few free cycles, reviews for this PR would be much appreciated. Thanks!

@prrace
Copy link
Contributor

prrace commented Mar 6, 2025

I am seeing an existing test fail on macOS with this change
java/awt/font/GlyphVector/NLGlyphTest.java

java NLGlyphTest
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
at java.desktop/sun.font.StandardGlyphVector.getGlyphCode(StandardGlyphVector.java:311)
at NLGlyphTest.main(NLGlyphTest.java:46)

This code
char[] chs = { '\n' };
GlyphVector lgv = font.layoutGlyphVector(frc, chs, 0, 1, 0);
results in a GV with zero glyphs, which isn't what I'd expect - that a string would have no glyphs at all.
Interestingly I think it is OK for some fonts, but not OK for most fonts.

@gredler
Copy link
Contributor Author

gredler commented Mar 7, 2025

OK, it looks like we've got a constant conflict between Java's INVISIBLE_GLYPH_ID and HarfBuzz's AAT::DELETED_GLYPH, both of which are 0xFFFF. This conflict only really matters when using an AAT font with a morx table, at which point hb_ot_substitute_post calls hb_aat_layout_remove_deleted_glyphs, which mis-identifies our invisible glyph as a deleted glyph which needs to be removed from the buffer.

I'm not sure where to take it from here. Java has been using 0xFFFF for quite some time to indicate invisible glyphs, so a change to the Java constant seems dangerous. There might be some wiggle room to use 0xFFFE, since the comments in CharToGlyphMapper seem to indicate that it should also be treated as an invisible glyph? The glyph removal in HarfBuzz is wrapped by an #ifndef HB_NO_AAT_SHAPE, so I supposed we could disable AAT shaping in HarfBuzz, but that also seems drastic.

Suggestions welcome!

public static final int INVISIBLE_GLYPH_ID = 0xffff;

hb_ot_substitute_post (const hb_ot_shape_context_t *c)

@prrace
Copy link
Contributor

prrace commented Mar 7, 2025

OK, it looks like we've got a constant conflict between Java's INVISIBLE_GLYPH_ID and HarfBuzz's AAT::DELETED_GLYPH, both of which are 0xFFFF. This conflict only really matters when using an AAT font with a morx table, at which point hb_ot_substitute_post calls hb_aat_layout_remove_deleted_glyphs, which mis-identifies our invisible glyph as a deleted glyph which needs to be removed from the buffer.

I'm not sure where to take it from here. Java has been using 0xFFFF for quite some time to indicate invisible glyphs, so a change to the Java constant seems dangerous. There might be some wiggle room to use 0xFFFE, since the comments in CharToGlyphMapper seem to indicate that it should also be treated as an invisible glyph? The glyph removal in HarfBuzz is wrapped by an #ifndef HB_NO_AAT_SHAPE, so I supposed we could disable AAT shaping in HarfBuzz, but that also seems drastic.

Suggestions welcome!

public static final int INVISIBLE_GLYPH_ID = 0xffff;

hb_ot_substitute_post (const hb_ot_shape_context_t *c)

That's unfortunate.
0xFFFF for invisible glyph has been in JDK for 25 years. It is probably baked into a lot of stuff. I would not want to change that unless forced to.
We also don't want HB to delegate AAT to CoreText .. I think we had problems in that area before HB supported it itself.
So I'm not sure where to take it from here either.

@prrace
Copy link
Contributor

prrace commented Mar 7, 2025

Looking across the code base, there are a number of places which look explicitly for 0xFFFF (INVISIBLE_GLYPH_ID) and somewhat fewer that look for >= 0xFFFE (INVISIBLE_GLYPHS) That's an interesting inconsistency in itself.
Also looking for 0xffff literals across the codebase is unsurprisingly tedious ..

The idea of 0xFFFE might be worth a shot but it maybe that we need to make a bunch of changes to code that looks just for INVISIBLE_GLYPH_ID, including in some tests, but it also might be that since this is macOS specific that it could be less risky - although since the result could still be used by shared code it isn't quite as constrained as you might hope.

@gredler
Copy link
Contributor Author

gredler commented Mar 7, 2025

I'm checking with the HarfBuzz guys to see if there's a possible fix on their end: harfbuzz/harfbuzz#5118

Unfortunately, it sounds like glyph ID 0xFFFF means "delete me" by convention in the AAT world, so it's a little complicated.

@gredler
Copy link
Contributor Author

gredler commented Mar 10, 2025

Good news: it looks like it was possible to resolve the 0xFFFF conflict on the HarfBuzz side by adjusting the HarfBuzz internal representation of AAT deleted glyphs. Amazingly quick turnaround from @behdad! I think we'll want to put this PR on hold until the next HarfBuzz upgrade. How often are dependencies like HarfBuzz upgraded? I saw we just completed one today, coincidentally.

@honkar-jdk
Copy link
Contributor

honkar-jdk commented Mar 10, 2025

@gredler Yes, JDK harfbuzz was just upgraded to v10.4.0 - #23910.

@prrace Do we consider patching the fix from upstream (harfbuzz/harfbuzz#5119) for this upgrade or can it wait till the next time harfbuzz is upgraded on jdk ?

@gredler
Copy link
Contributor Author

gredler commented Mar 10, 2025

@honkar-jdk Thanks for the info -- note that it's actually a combination of two HB PRs (harfbuzz/harfbuzz#5119 and harfbuzz/harfbuzz#5126), and it sounds like they plan to cut a release with the fix later this week.

@prrace
Copy link
Contributor

prrace commented Mar 11, 2025

Without even yet having looked at the fix in upstream I'll say something
In general I am not in favour of taking point fixes.
(1) we are supposed to say what version of [upstream library] is in JDK and where do you draw the line of we have version N, vs N+1.
(2) un-released patches may not be properly tested.
(3) They may be reverted, and then where are we ?

Upgrading harfbuzz is not frequent (we don't need busy work) but if there's a clear and valuable reason, it can be done.
In this case, there should be very few (weeks vs years) upstream changes, so I'd support doing it if it makes sense.
But I need to look at the specifics of the upstream fix.

@prrace
Copy link
Contributor

prrace commented Mar 11, 2025

So let's see if we can integrate a new HB release in maybe 6 weeks from now, and then take up this PR again.

@gredler
Copy link
Contributor Author

gredler commented Mar 11, 2025

@honkar-jdk @prrace Makes sense -- thanks for having a look, and I'll check back in on this one later in April.

@prrace
Copy link
Contributor

prrace commented Apr 3, 2025

@honkar-jdk pls remember we need an updated harfbuzz for this PR to proceed.

@honkar-jdk
Copy link
Contributor

@honkar-jdk pls remember we need an updated harfbuzz for this PR to proceed.

Noted. Thank you for the reminder.

@behdad
Copy link
Contributor

behdad commented Apr 3, 2025

Hi @behdad, thanks for making a comment in an OpenJDK project!

All comments and discussions in the OpenJDK Community must be made available under the OpenJDK Terms of Use. If you already are an OpenJDK Author, Committer or Reviewer, please click here to open a new issue so that we can record that fact. Please Use "Add GitHub user behdad" for the summary.

If you are not an OpenJDK Author, Committer or Reviewer, simply check the box below to accept the OpenJDK Terms of Use for your comments.

Your comment will be automatically restored once you have accepted the OpenJDK Terms of Use.

Copy link
Contributor

@honkar-jdk honkar-jdk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gredler Tested your fix by applying your changes on top of harfbuzz upgrade - #24973 . Initial testing of your fix with IgnoredWhitespaceTest.java test and NLGlyphTest.java looks good. I haven't done a full fledged testing yet.

This PR fixes the same issue observed on another test - java/awt/font/TextLayout/TestControls.java - JDK-8353187. You can link the additional JBS ID to this PR with /issue add 8353187

I will be integrating harfbuzz upgrade PR soon after which you can sync your branch with mainline to test it on your end.

@gredler
Copy link
Contributor Author

gredler commented May 1, 2025

/issue add 8353187

@openjdk
Copy link

openjdk bot commented May 1, 2025

@gredler
Adding additional issue to issue list: 8353187: Test TextLayout/TestControls fails on macOS: width of 0x9, 0xa, 0xd isn't zero.

@gredler
Copy link
Contributor Author

gredler commented May 1, 2025

@honkar-jdk Great, thanks! I'll keep an eye on the HarfBuzz upgrade PR and sync + test once it has been integrated.

Comment on lines +135 to +138
private static boolean isIgnorableWhitespace(int code) {
return code == 0x0009 || code == 0x000a || code == 0x000d;
}

Copy link
Contributor

@honkar-jdk honkar-jdk May 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@prrace @gredler
Following is an existing issue NOT related to the recent harfbuzz upgrade PR nor this issue fix but it is similar in nature so a separate JBS issue can be created to handle it. I wanted to discuss it before integrating the HB upgrade tomorrow.

There seems to be similar issue on windows and linux where the hexcode - 0x2028 (line separator) and 0x2029 (paragraph separator) are shown with a non-zero advance - /java/awt/font/TextLayout/TestControls.java

On Windows:

image

On Linux:

image

If adding these codes (0x2028, 0x2029) to platform specific src code (I believe it is NativeGlyphMapper (linux) and WPathGraphics (windows)) fixes the above issue then should we consider moving isIgnorableWhitespace() to common class - CharToGlyphMapper instead of macOS specific src?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think right now \t \r \n handling is done separately for each platform, and I think also separately for screen display vs. printing.

As an example, this fix addresses the screen display side of things for macOS, but printing on macOS needs to be fixed separately (as can be seen when you run test/jdk/java/awt/print/PrinterJob/PrintTextTest.java). I had a quick look at the corresponding macOS printing issue a few months ago and the fix wasn't obvious, especially given how that part of the code is organized (not really 1-to-1 with the other platforms).

So I think \t \r \n handling is repeated quite a bit, and if we wanted to handle 0x2028 and 0x2029 exactly like we do \t \r \n then we'd need to change multiple places right now.

should we consider moving isIgnorableWhitespace() to common class - CharToGlyphMapper instead of macOS specific src

That sounds like it could be a good idea, but I don't have a sense of how likely it is to cause breakage elsewhere -- it'd be a matter of "do it and check for test regressions". Maybe you have a better gut feel for the likelihood of unintended side-effects?

Note these two characters (0x2028 and 0x2029) are not default-ignorable, though. It sounds like we just don't want to render their glyphs, if they have any (similar to \t \r \n, which are also not default-ignorable).

Over on PR #24412 Nikita has suggested an interesting change to CharToGlyphMapper and subclasses which might make this change easier, but I'd like some feedback from a reviewer or two before moving forward. The PR is complete from my POV, but we need some design direction (good as is, or needs tweaks, or need to use a different approach entirely, etc).

Copy link
Contributor

@honkar-jdk honkar-jdk May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created a new JBS to track the issue on windows and linux - JDK-8356803 - Test TextLayout/TestControls fails on windows & linux: line and paragraph separator show a non-zero advance

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seems to be similar issue on Windows and Linux where the hexcode - 0x2028 (line separator) and 0x2029 (paragraph separator) are shown with a non-zero advance - /java/awt/font/TextLayout/TestControls.java

Are these characters new? I can't remember there were any characters with non-zero width on Linux and Windows. I ran this test (or its previous version) on each HarfBuzz update.

No, they were these characters are part of the existing test.

I cannot reproduce this problem with 21.0.6. Nor is it reproducible with 21.0.7, the latest GA version of Java 21.

Yet I the same picture as you attached on Windows on a recent build of mainline.

If that's the case, it seems to be regression rather than an existing issue.

Copy link
Member

@aivanov-jdk aivanov-jdk May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also submitted JDK-8356812: Create an automated version of TextLayout/TestControls.

Verifying that the widths of all control characters is zero can be automated; the manual version can exist along-side to ensure all the characters are rendered as expected.

Copy link
Contributor

@honkar-jdk honkar-jdk May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aivanov-jdk Thanks for testing on older versions of JDK. I'm checking if this issue was caused by harfbuzz upgrade or recent code changes to JDK.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above issue is replicable after jdk25+b10 and was introduced by changeset related to JDK-8208377 - Soft hyphens render if not using TextLayout , #22670

Copy link
Contributor

@honkar-jdk honkar-jdk May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gredler This issue JDK-8356803 - Test TextLayout/TestControls fails on windows & linux line and paragraph separator show a non-zero advance, is re-assigned to you since it is a regression of JDK-8208377

@prrace
Copy link
Contributor

prrace commented May 7, 2025

I don't think
NativeGlyphMapper - used only when we needs the Xserver to render, or
WPathGraphics - a windows printing class

are where I'd expect to find anything that could impact the TestControls test.

But I agree with the general points

  • \t \r \n handling can be platform-specific
  • macOS printing is very different

The other PR still needs a proper look on my part

I suggest we get this PR pushed but first I'll submit a test job .. unless Harshitha already did that ?

@honkar-jdk
Copy link
Contributor

honkar-jdk commented May 7, 2025

I suggest we get this PR pushed but first I'll submit a test job .. unless Harshitha already did that ?

I haven't done a complete CI run along with this PR changes.

@honkar-jdk
Copy link
Contributor

As an example, this fix addresses the screen display side of things for macOS, but printing on macOS needs to be fixed separately (as can be seen when you run test/jdk/java/awt/print/PrinterJob/PrintTextTest.java). I had a quick look at the corresponding macOS printing issue a few months ago and the fix wasn't obvious, especially given how that part of the code is organized (not really 1-to-1 with the other platforms).

I agree it was not as straightforward to see where to add the fix for windows and linux platforms.

@gredler
Copy link
Contributor Author

gredler commented May 8, 2025

I haven't had time to test this latest merge on Windows, but my local testing of the current state on Linux and macOS looks good, so I'm cautiously optimistic.

@openjdk openjdk bot added the ready Pull request is ready to be integrated label May 8, 2025
Copy link
Contributor

@honkar-jdk honkar-jdk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI Testing looks good. Didn't observe any regressions in the couple of manual tests that I ran with this fix. LGTM

Copy link
Member

@aivanov-jdk aivanov-jdk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, except for formatting suggestions.

Co-authored-by: Alexey Ivanov <alexey.ivanov@oracle.com>
@openjdk openjdk bot removed the ready Pull request is ready to be integrated label May 9, 2025
@openjdk openjdk bot added the ready Pull request is ready to be integrated label May 11, 2025
@gredler
Copy link
Contributor Author

gredler commented May 12, 2025

@prrace @honkar-jdk I've incorporated a few minor formatting changes requested by Alexey. Please let me know if you have any concerns with these latest tweaks, otherwise I will go ahead and mark as ready for integration tomorrow. Thanks!

Copy link
Contributor

@honkar-jdk honkar-jdk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@gredler
Copy link
Contributor Author

gredler commented May 13, 2025

/integrate

@openjdk openjdk bot added the sponsor Pull request is ready to be sponsored label May 13, 2025
@openjdk
Copy link

openjdk bot commented May 13, 2025

@gredler
Your change (at version 2e37be1) is now ready to be sponsored by a Committer.

@aivanov-jdk
Copy link
Member

/sponsor

@openjdk
Copy link

openjdk bot commented May 13, 2025

Going to push as commit 85db463.
Since your change was applied there have been 86 commits pushed to the master branch:

Your commit was automatically rebased without conflicts.

@openjdk openjdk bot added the integrated Pull request has been integrated label May 13, 2025
@openjdk openjdk bot closed this May 13, 2025
@openjdk openjdk bot removed ready Pull request is ready to be integrated rfr Pull request is ready for review sponsor Pull request is ready to be sponsored labels May 13, 2025
@openjdk
Copy link

openjdk bot commented May 13, 2025

@aivanov-jdk @gredler Pushed as commit 85db463.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

client client-libs-dev@openjdk.org integrated Pull request has been integrated

Development

Successfully merging this pull request may close these issues.

5 participants