IDEA-83645 Editor: Correct '`' (backtick) symbol drawing inside the editor

1. Checking if a font contains invalid glyphs on font info initialisation;
2. Ending painting iteration after the faulty glyphs (if any);
This commit is contained in:
Denis.Zhdanov
2012-03-29 14:10:31 +04:00
parent ebc10c7cd2
commit 0700053f70
3 changed files with 71 additions and 2 deletions
@@ -73,6 +73,8 @@ import org.picocontainer.MutablePicoContainer;
import javax.swing.*;
import java.awt.*;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
@@ -2628,6 +2628,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
int myCount = 0;
@NotNull final FontInfo myFontType;
final boolean myHasBreakSymbols;
final int spaceWidth;
@Nullable private char[] myLastData;
@@ -2635,6 +2636,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
private CachedFontContent(@NotNull FontInfo fontInfo) {
myFontType = fontInfo;
spaceWidth = fontInfo.charWidth(' ', myEditorComponent);
myHasBreakSymbols = fontInfo.hasGlyphsToBreakDrawingIteration();
}
private void flushContent(@NotNull Graphics g) {
@@ -2666,7 +2668,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
final int lastCount = count - 1;
final Color lastColor = color[lastCount];
if (_data == myLastData && _start == ends[lastCount] && (_color == null || lastColor == null || _color.equals(lastColor))
&& _y == y[lastCount] /* there is a possible case that vertical position is adjusted because of soft wrap */)
&& _y == y[lastCount] /* there is a possible case that vertical position is adjusted because of soft wrap */
&& (!myHasBreakSymbols || !myFontType.getSymbolsToBreakDrawingIteration().contains(_data[ends[lastCount] - 1])))
{
ends[lastCount] = _end;
if (lastColor == null) color[lastCount] = _color;
@@ -3054,6 +3057,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
myForceRefreshFont = true;
}
endX += charWidth;
if (newFont.hasGlyphsToBreakDrawingIteration() && newFont.getSymbolsToBreakDrawingIteration().contains(c)) {
drawCharsCached(g, text, start, j + 1, x, y, fontType, fontColor);
x = endX;
start = j + 1;
}
}
if (!(x < clip.x && endX < clip.x || x > clip.x + clip.width && endX > clip.x + clip.width)) {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,14 +17,21 @@ package com.intellij.openapi.editor.impl;
import gnu.trove.TIntHashSet;
import org.intellij.lang.annotations.JdkConstants;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.image.BufferedImage;
/**
* @author max
*/
public class FontInfo {
private final TIntHashSet mySymbolsToBreakDrawingIteration = new TIntHashSet();
private final String myFamilyName;
private final Font myFont;
private final int mySize;
@@ -32,12 +39,63 @@ public class FontInfo {
private final TIntHashSet mySafeCharacters = new TIntHashSet();
private FontMetrics myFontMetrics = null;
private final int[] charWidth = new int[128];
private final boolean myHasGlyphsToBreakDrawingIteration;
public FontInfo(final String familyName, final int size, @JdkConstants.FontStyle int style) {
myFamilyName = familyName;
mySize = size;
myStyle = style;
myFont = new Font(familyName, style, size);
parseProblemGlyphs();
myHasGlyphsToBreakDrawingIteration = !mySymbolsToBreakDrawingIteration.isEmpty();
}
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);
}
}
}
/**
* We've experienced a problem that particular symbols from particular font are represented really weird
* by the IJ editor (IDEA-83645).
* <p/>
* Eventually it was found out that outline font glyphs can have a 'y advance', i.e. instruction on how the subsequent
* glyphs location should be adjusted after painting the current glyph. In terms of java that means that such a problem
* glyph should be the last symbol at the {@link Graphics#drawChars(char[], int, int, int, int) text drawing iteration}.
* <p/>
* Hopefully, such glyphs are exceptions from the normal processing, so, this method allows to answer whether a font
* {@link #getFont() referenced} by the current object has such a glyph.
*
* @return true if the {@link #getFont() target font} has problem glyphs; <code>false</code> otherwise
*/
public boolean hasGlyphsToBreakDrawingIteration() {
return myHasGlyphsToBreakDrawingIteration;
}
/**
* @return unicode symbols which glyphs {@link #hasGlyphsToBreakDrawingIteration() have problems}
* at the {@link #getFont() target font}.
*/
@NotNull
public TIntHashSet getSymbolsToBreakDrawingIteration() {
return mySymbolsToBreakDrawingIteration;
}
public boolean canDisplay(char c) {