Community
Participate
Working Groups
I20041109 I have a case where I draw onto a GC using similar to this: gc.fillRectangle(x, y, width, lineheight); gc.drawString(s, x, y, true); However the string is printed such that its upper bound is right at 'y' instead of leaving some room as usual. I will attach a screenshot. I cannot reproduce the problem when using the snippet below - I am not sure whether the problem stems from using multiple nested Canvas'? Please comment. Snippet that does show the expected behavior: import org.eclipse.swt.SWT; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.*; import org.eclipse.swt.widgets.*; public class MotifStringDrawing { public static void main (String [] args) { final Display display = new Display (); final Shell shell = new Shell (display, SWT.SHELL_TRIM); Rectangle rectangle= shell.computeTrim(50, 50, 260, 460); shell.setBounds(rectangle); shell.open (); final Composite composite = new Composite (shell, SWT.BORDER); composite.setBounds(30, 30, 200, 400); int xorigin= 20; int yorigin= 20; final int width= 20; Canvas canvas= new Canvas(composite, SWT.NONE); canvas.setBounds(xorigin, yorigin, width, 200); canvas.addPaintListener(new PaintListener() { public void paintControl(org.eclipse.swt.events.PaintEvent e) { GC gc = e.gc; FontMetrics metrics= gc.getFontMetrics(); int lineheight= metrics.getHeight(); gc.setForeground(display.getSystemColor(SWT.COLOR_BLACK)); int indent= 4; for (int i= 0; i < 10; i++) { gc.setBackground(display.getSystemColor(i + SWT.COLOR_RED)); gc.fillRectangle(0, i*lineheight , width, lineheight); gc.drawString(Integer.toString(i), indent, i*lineheight, true); } } }); while (!shell.isDisposed ()) { if (!display.readAndDispatch ()) display.sleep (); } display.dispose (); } }
Created attachment 15770 [details] screenshot the line number ruler is a canvas. both the colored background rectangle and the linenumber are drawn using the same y offset.
Created attachment 15773 [details] screenshot showing the discrepancy between styled text string drawing and the ruler
Make sure the snippet and the ruler on the workbench are using the same font. This looks like a font with bad metrics case.
In the real code are you guys using: FontMetrics metrics= gc.getFontMetrics(); int lineheight= metrics.getHeight(); or: int lineheight= styledText.getLineHeight() ? Note that later is the right code, gc#getFontMetrics returns only the metrics of one font and styledText.getLineHeight will compute the metrics of all the fonts in use by the styledtext (normal, bold, italic, and bold-italic) and return the max ascent + max desent.
we are using the latter to compute the line heigth - when drawing the line numbers, we compute an additional offset to draw the text using the following code: /** * Returns the difference between the baseline of the widget and the * baseline as specified by the font for <code>gc</code>. When drawing * line numbers, the returned bias should be added to obtain text line up on * the correct base line of the text widget. * * @param gc the <code>GC</code> to get the font metrics from * @return the baseline bias to use when drawing text that is line up with * <code>fCachedTextWidget</code> */ private int getBaselineBias(GC gc) { /* * https://bugs.eclipse.org/bugs/show_bug.cgi?id=62951 * widget line height may be more than the font height used for the * linenumbers, since font styles (bold, italics...) can have larger * font metrics than the simple font used for the numbers. */ int widgetBaseline= fCachedTextWidget.getBaseline(); FontMetrics fm = gc.getFontMetrics(); int fontBaseline = fm.getAscent() + fm.getLeading(); Assert.isTrue(widgetBaseline >= fontBaseline); int baselineBias= widgetBaseline - fontBaseline; return baselineBias; } In the motif case, there never is any baseline bias which may be due to the fact that the StyledText implementation does not take into account 'leading' at all?
Okay, the code in eclipse is right. "StyledText implementation does not take into account 'leading' at all?" This is not correct, StyledText considers the leading. The problem is in the fontBaseline. You compute it using gc.getFontMetrics(), which returns the max ascent + max leading of all font structs in the font list, in TextLayout I use XmBaseline(fontList, string) that returns only the ascent + leading of the font struct what will be used to draw 'string'. Not sure, I can fix this problem in GC#drawString for you or add a new API in SWT (something like a getMetrics that takes a string), the problem is that this new API would only make sense in Motif where a font is a list of list of font structs and you don't know which font will be used till you know the string.
> "StyledText implementation does not take into account 'leading' at all?" > This is not correct, StyledText considers the leading. I am wrong, sorry. I had just quickly looked at StyledTextRenderer and saw that it only stores the ascent, but did not look how it computes it... > The problem is in the fontBaseline. You compute it using gc.getFontMetrics(), > which returns the max ascent + max leading of all font structs in the font > list Would there be an easy way for me to measure this? Or create a new font with just the FontData I am going to use. Say, something like this: Font font= gc.getFont(); boolean useSimpleFont= false; FontData[] datas= font.getFontData(); for (int i= 0; i < datas.length; i++) { if (SWT.SIMPLE == datas[i].getStyle()) { font= new Font(display, datas[i]); useSimpleFont= true; break; } } if (useSimpleFont) gc.setFont(font); FontMetrics fm = gc.getFontMetrics(); if (useSimpleFont) font.dispose(); // measure offset as above
It won't work Tom, this needs to be fixed in SWT. I think the right thing is to fix drawString/drawText, but problem is that the fix will make them slower (possible much slower), maybe I'll have to cache the max baseline for a font in GCData or something.
The fix makes drawString 6x slower (tested in UTF-8) With a cache it works much better (1.1x slower).
Any updates on this? It seems the same problem appears on linux-gtk as well...
ping.
GC metrics on Motif are a bit off. This simple code doesn't work: canvas.addPaintListener(new PaintListener() { public void paintControl(PaintEvent e) { GC gc = e.gc; FontMetrics metrics= gc.getFontMetrics(); gc.drawString("PQpq", xOffset, yOffset, true); int baseline = metrics.getAscent() + metrics.getLeading(); gc.drawLine (xOffset, yOffset+baseline, 300, yOffset+baseline); } }); The line is not at the baseline. Inside drawText/drawString we need to fix y, something like: y = y + fontBaseline - textBaseline; where textBaseline is: OS.XmStringBaseline(data.font.handle, data.xmString); and fontBaseline can be computed with: int getFontBaseline () { int fontList = data.font.handle; /* Create a font context to iterate over each element in the font list */ int [] buffer = new int [1]; if (!OS.XmFontListInitFontContext (buffer, fontList)) { SWT.error(SWT.ERROR_NO_HANDLES); } int context = buffer [0]; /* Values discovering during iteration */ int ascent = 0; XFontStruct fontStruct = new XFontStruct (); int fontListEntry; int [] fontStructPtr = new int [1]; int [] fontNamePtr = new int [1]; /* Go through each entry in the font list. */ while ((fontListEntry = OS.XmFontListNextEntry (context)) != 0) { int fontPtr = OS.XmFontListEntryGetFont (fontListEntry, buffer); if (buffer [0] == 0) { /* FontList contains a single font */ OS.memmove (fontStruct, fontPtr, XFontStruct.sizeof); ascent = Math.max(ascent, fontStruct.ascent); } else { /* FontList contains a fontSet */ int nFonts = OS.XFontsOfFontSet (fontPtr, fontStructPtr, fontNamePtr); int [] fontStructs = new int [nFonts]; OS.memmove (fontStructs, fontStructPtr [0], nFonts * 4); /* Go through each fontStruct in the font set */ for (int i=0; i<nFonts; i++) { OS.memmove (fontStruct, fontStructs[i], XFontStruct.sizeof); ascent = Math.max(ascent, fontStruct.ascent); } } } OS.XmFontListFreeFontContext (context); return ascent; } =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Two problems: 1) performance! 2) TextLayout already had the code to "fix" y, it would have to be removed.
The other option is not to fix gc on motif. but change the application code to draw using TextLayout, I loaded org.eclipse.jface.text and changed LineNumberRulerColumn#doPaint as follow: void doPaint(GC gc, ILineRange visibleLines) { Display display= fCachedTextWidget.getDisplay(); // draw diff info int y= -JFaceTextUtil.getHiddenTopLinePixels(fCachedTextWidget); gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE)); int lastLine= end(visibleLines); for (int line= visibleLines.getStartLine(); line < lastLine; line++) { int widgetLine= JFaceTextUtil.modelLineToWidgetLine(fCachedTextViewer, line); if (widgetLine == -1) continue; int offset= fCachedTextWidget.getOffsetAtLine(widgetLine); int lineHeight= fCachedTextWidget.getLineHeight(offset); int ascent= fCachedTextWidget.getBaseline(offset); String s= createDisplayString(line); int indentation= fIndentation[s.length()]; TextLayout layout= new TextLayout(display); layout.setFont(fCachedTextWidget.getFont()); layout.setText(s); layout.setAscent(ascent); layout.setDescent(lineHeight - ascent); layout.draw(gc, indentation, y); layout.dispose(); y += lineHeight; } } *-*--*-*-*-*-*-*-*-*-*-* Doing this I think you don't even need that getBaselineBias anymore.
>The other option is not to fix gc on motif. but change the application code to >draw using TextLayout, What about all other SWT clients using this? Tom, please check whether we could use Felipe's code.
Hm, just tested the TextLayout version, and while it appears to work, I also see a lot more flashing going on. This is still true if I create the TextLayout in createControl and reuse it for painting, and is not influenced by doubleBuffering (which we'll probably get rid off anyway). Will investigate some more.
> What about all other SWT clients using this? The problem is there and it should be fixed. I'll talk about this to SSQ and when comes back. > I also see a lot more flashing going on. Hmm, I don't understand why. TextLayout draws transparent (it doesn't erase background). I don't know what can be causing this flashing.
I found a similar problem with GC.drawString which fails on both WindowsXP and Linux-GTK. See bug 165640.
Your bug has been moved to triage, visit http://www.eclipse.org/swt/triage.php for more info.
Linux Motif no longer supported.