Table of contents
- Problem description;
- Step 1: Distill a sample;
- Step 2: Client code analysis;
- Step 3: Standard java library analysis;
- Step 4: Debug native code;
- Step 5: Dead end?;
- Step 6: Happy end;
Problem description
IntelliJ IDEA's editor uses AWT primitives for all drawing (text, backgrounds, effects etc). Recently we've noticed that there is a problem with the '`' (backtick) symbol drawing using Inconsolata font. It looks as below (at least under Linux):
Step 1: Distill a sample
Ok, we see that the problem is observed during IntelliJ IDEA drawing but its rather inconvenient to use it for nailing the error down. Reasons: it takes time to start the IDE under IDE after the change; IJ uses a sophisticated processing on top of AWT drawing primitives; it's hard to submit the problem to Oracle if we need IJ to reproduce it etc.
So, I've built a simple Swing application that illustrates the problem - java web start link (assembled binary plus sources are available here).
Current assumption: there is a problem with the font
Step 2: Client code analysis
I've worked around the sample and have made the following observations:
- The problem occurs when we call Graphics.drawChars() with a string which length is more than one;
- All symbols that go after the backtick are displayed incorrectly;
Example:

Artifacts: real time web start start, assembled jar.
Conclusion: need to debug the processing
Step 3: Standard java library analysis
I've downloaded openjdk source bundle and configured it inside IntelliJ IDEA.
Further debugging shows that both types of drawing (draw a whole string VS draw by single symbols) use the same java state which is delivered to the native call DrawGlyphList::DrawGlyphList():

Conclusion: I need to debug the native code in order to find out the problem that occurs there.
Step 4: Debug native code
Eventually I was able to build local jdk with debug info. Unfortunately, the problem is not reproduced under it. I'm mad.
Checked that the problem is not reproduced under openjdk6 as well. Looks like sun/oracle jre problem.
Step 5: Dead end?
We can't find out the source of the problem, so, the only choice left is to provide a workaround at the client side. I don't how to call it (experience? luck?) but I managed to found another Inconsolata font that renders correctly - sample (tarball). That means that I can analyse the difference between the 'good' and 'bad' glyphs.
Step 6: Happy end
I've found out ability to detect problem glyphs from java - the property is GlyphMetrics.AdvanceY. The javadoc mentions 'advance' as follows:
'The advance width indicates the position at which AWT should place the next character'
Unfortunately, it mentions 'advance' attribute only in 'horizontal' perspective. Here we got a situation when the 'bad glyph' had non-zero 'y advance'. That produced exactly the observed effect - all subsequent symbols drawn at the same iteration were shifted vertically.
The solution was to check the font on the initialization an hold information about the problem glyphs. It looks as follows:
private void parseProblemGlyphs() {
BufferedImage buffer = new BufferedImage(20, 20, BufferedImage.TYPE_INT_RGB);
final Graphics graphics = buffer.getGraphics();
if (!(graphics instanceof Graphics2D)) {
return;
}
final FontRenderContext context = ((Graphics2D)graphics).getFontRenderContext();
char[] charBuffer = new char[1];
for (char c = 0; c < 128; c++) {
if (!myFont.canDisplay(c)) {
continue;
}
charBuffer[0] = c;
final GlyphVector vector = myFont.createGlyphVector(context, charBuffer);
final float y = vector.getGlyphMetrics(0).getAdvanceY();
if (Math.round(y) != 0) {
mySymbolsToBreakDrawingIteration.add(c);
}
}
}
I'd call this happy end :)

Cool!
ReplyDeleteBut I still have this problem in IntelliJ 11.1.3 on Linux, with the Fedora Inconsolata font.
Is this workaround going to be available in IDEA in the future?
AFAIR, the fix is provided for the v.12 branch. Can you check that with the latest v.12 EAP?
ReplyDelete