Skip to content

Commit e9cc756

Browse files
committed
Issue #932: [win32] implementation for StyledText.setFixedLineMetrics
Signed-off-by: Alexandr Miloslavskiy <[email protected]>
1 parent d6a8815 commit e9cc756

File tree

1 file changed

+110
-9
lines changed
  • bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics

1 file changed

+110
-9
lines changed

bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java

Lines changed: 110 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ public final class TextLayout extends Resource {
6464
IMLangFontLink2 mLangFontLink2;
6565
int verticalIndentInPoints;
6666

67+
private MetricsAdapter metricsAdapter = new MetricsAdapter();
68+
6769
static final char LTR_MARK = '\u200E', RTL_MARK = '\u200F';
6870
static final int SCRIPT_VISATTR_SIZEOF = 2;
6971
static final int GOFFSET_SIZEOF = 8;
@@ -185,6 +187,100 @@ public String toString () {
185187
}
186188
}
187189

190+
private static class MetricsAdapter {
191+
private TEXTMETRIC wantMetricsInPixels;
192+
193+
// Optimization: produce less GC garbage in frequent calls
194+
TEXTMETRIC realMetricsInPixels = new TEXTMETRIC();
195+
196+
public boolean isFixedMetrics() {
197+
return (wantMetricsInPixels != null);
198+
}
199+
200+
public void setFixedLineMetrics(FontMetrics metrics) {
201+
if (metrics == null) {
202+
wantMetricsInPixels = null;
203+
return;
204+
}
205+
206+
TEXTMETRIC result = new TEXTMETRIC();
207+
result.tmAscent = metrics.handle.tmAscent;
208+
result.tmDescent = metrics.handle.tmDescent;
209+
result.tmHeight = metrics.handle.tmHeight;
210+
result.tmInternalLeading = metrics.handle.tmInternalLeading;
211+
result.tmAveCharWidth = 0;
212+
213+
wantMetricsInPixels = result;
214+
}
215+
216+
public int calcYAdjust(TEXTMETRIC realMetricsInPixels) {
217+
// Adjust text position so that baseline is preserved:
218+
// * Taller characters may go out of line bounds.
219+
// * Shorter characters will fit and also stay on baseline.
220+
//
221+
// Note that without fixed line metrics mode, SWT doesn't
222+
// align baseline. When different scripts are mixed, this
223+
// causes baseline to "jump" up and down. I believe that
224+
// this is an oversight.
225+
return (wantMetricsInPixels.tmAscent - realMetricsInPixels.tmAscent);
226+
}
227+
228+
public int calcYAdjust(long hdc) {
229+
OS.GetTextMetrics(hdc, realMetricsInPixels);
230+
return calcYAdjust(realMetricsInPixels);
231+
}
232+
233+
public boolean GetTextMetrics(long hdc, TEXTMETRIC lptm) {
234+
// Even in fixed mode, still call original to get `tmAveCharWidth` etc.
235+
boolean ret = OS.GetTextMetrics(hdc, lptm);
236+
if (!ret) return false;
237+
238+
if (isFixedMetrics()) {
239+
// Force desired line metrics
240+
lptm.tmAscent = wantMetricsInPixels.tmAscent;
241+
lptm.tmDescent = wantMetricsInPixels.tmDescent;
242+
lptm.tmHeight = wantMetricsInPixels.tmHeight;
243+
lptm.tmInternalLeading = wantMetricsInPixels.tmInternalLeading;
244+
}
245+
246+
return ret;
247+
}
248+
249+
public int GetOutlineTextMetrics(long hdc, int cbData, OUTLINETEXTMETRIC lpOTM) {
250+
// Even in fixed mode, still call original to get `tmAveCharWidth` etc.
251+
int ret = OS.GetOutlineTextMetrics(hdc, cbData, lpOTM);
252+
if (0 == ret) return 0;
253+
254+
if (isFixedMetrics()) {
255+
TEXTMETRIC lptm = lpOTM.otmTextMetrics;
256+
257+
final int yAdjust = calcYAdjust(lptm);
258+
259+
// Force desired line metrics
260+
lptm.tmAscent = wantMetricsInPixels.tmAscent;
261+
lptm.tmDescent = wantMetricsInPixels.tmDescent;
262+
lptm.tmHeight = wantMetricsInPixels.tmHeight;
263+
lptm.tmInternalLeading = wantMetricsInPixels.tmInternalLeading;
264+
265+
// Also adjust underline/strikeout positions in the same
266+
// way as text position will be adjusted when painting it
267+
lpOTM.otmsUnderscorePosition += yAdjust;
268+
lpOTM.otmsStrikeoutPosition += yAdjust;
269+
}
270+
271+
return ret;
272+
}
273+
274+
public int ScriptTextOut(long hdc, long psc, int x, int y, int fuOptions, RECT lprc, SCRIPT_ANALYSIS psa, long pwcReserved, int iReserved, long pwGlyphs, int cGlyphs, long piAdvance, long piJustify, long pGoffset) {
275+
if (isFixedMetrics()) {
276+
final int yAdjust = calcYAdjust(hdc);
277+
y += yAdjust;
278+
}
279+
280+
return OS.ScriptTextOut(hdc, psc, x, y, fuOptions, lprc, psa, pwcReserved, iReserved, pwGlyphs, cGlyphs, piAdvance, piJustify, pGoffset);
281+
}
282+
}
283+
188284
/**
189285
* Constructs a new instance of this class on the given device.
190286
* <p>
@@ -440,7 +536,7 @@ void computeRuns (GC gc) {
440536
if (lineRunCount == 1 && (i == allRuns.length - 1 || !run.softBreak)) {
441537
TEXTMETRIC lptm = new TEXTMETRIC();
442538
OS.SelectObject(srcHdc, getItemFont(run));
443-
OS.GetTextMetrics(srcHdc, lptm);
539+
metricsAdapter.GetTextMetrics(srcHdc, lptm);
444540
run.ascentInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmAscent);
445541
run.descentInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmDescent);
446542
ascentInPoints = Math.max(ascentInPoints, run.ascentInPoints);
@@ -1067,11 +1163,11 @@ RECT drawRunText(long hdc, StyleItem run, RECT rect, int baselineInPixels, int c
10671163
}
10681164
}
10691165
OS.SetTextColor(hdc, color);
1070-
OS.ScriptTextOut(hdc, run.psc, x, y, 0, null, run.analysis , 0, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets);
1166+
metricsAdapter.ScriptTextOut(hdc, run.psc, x, y, 0, null, run.analysis , 0, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets);
10711167
if (partialSelection) {
10721168
getPartialSelection(run, selectionStart, selectionEnd, rect);
10731169
OS.SetTextColor(hdc, selectionColor);
1074-
OS.ScriptTextOut(hdc, run.psc, x, y, OS.ETO_CLIPPED, rect, run.analysis , 0, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets);
1170+
metricsAdapter.ScriptTextOut(hdc, run.psc, x, y, OS.ETO_CLIPPED, rect, run.analysis , 0, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets);
10751171
}
10761172
return fullSelection || partialSelection ? rect : null;
10771173
}
@@ -1081,8 +1177,14 @@ RECT drawRunTextGDIP(long graphics, StyleItem run, RECT rect, long gdipFont, int
10811177
boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
10821178
boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end;
10831179
boolean partialSelection = hasSelection && !fullSelection && !(selectionStart > end || run.start > selectionEnd);
1180+
1181+
// Note from a passerby: it seems that 'Graphics::DrawDriverString'
1182+
// puts baseline at requested position. This is different from GDI
1183+
// rendering (such as ScriptTextOut()) which put top of the character
1184+
// at requested position.
10841185
int drawY = rect.top + baselineInPixels;
10851186
if (run.style != null && run.style.rise != 0) drawY -= DPIUtil.autoScaleUp(getDevice(), run.style.rise);
1187+
10861188
int drawX = rect.left;
10871189
long brush = color;
10881190
if (fullSelection) {
@@ -1998,7 +2100,7 @@ public FontMetrics getLineMetrics (int lineIndex) {
19982100
long srcHdc = OS.CreateCompatibleDC(hDC);
19992101
TEXTMETRIC lptm = new TEXTMETRIC();
20002102
OS.SelectObject(srcHdc, font != null ? font.handle : device.systemFont.handle);
2001-
OS.GetTextMetrics(srcHdc, lptm);
2103+
metricsAdapter.GetTextMetrics(srcHdc, lptm);
20022104
OS.DeleteDC(srcHdc);
20032105
device.internal_dispose_GC(hDC, null);
20042106

@@ -3108,8 +3210,7 @@ public void setDescent (int descent) {
31083210
* @since 3.125
31093211
*/
31103212
public void setFixedLineMetrics (FontMetrics metrics) {
3111-
if (metrics == null) return;
3112-
SWT.error(SWT.ERROR_NOT_IMPLEMENTED);
3213+
metricsAdapter.setFixedLineMetrics(metrics);
31133214
}
31143215

31153216
/**
@@ -3798,7 +3899,7 @@ long metaFileEnumProc (long hDC, long table, long record, long nObj, long lpData
37983899
OUTLINETEXTMETRIC lotm = null;
37993900
if (style.underline || style.strikeout) {
38003901
lotm = new OUTLINETEXTMETRIC();
3801-
if (OS.GetOutlineTextMetrics(hdc, OUTLINETEXTMETRIC.sizeof, lotm) == 0) {
3902+
if (metricsAdapter.GetOutlineTextMetrics(hdc, OUTLINETEXTMETRIC.sizeof, lotm) == 0) {
38023903
lotm = null;
38033904
}
38043905
}
@@ -3819,7 +3920,7 @@ long metaFileEnumProc (long hDC, long table, long record, long nObj, long lpData
38193920
lptm = lotm.otmTextMetrics;
38203921
} else {
38213922
lptm = new TEXTMETRIC();
3822-
OS.GetTextMetrics(hdc, lptm);
3923+
metricsAdapter.GetTextMetrics(hdc, lptm);
38233924
}
38243925
run.ascentInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmAscent);
38253926
run.descentInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmDescent);
@@ -3840,7 +3941,7 @@ long metaFileEnumProc (long hDC, long table, long record, long nObj, long lpData
38403941
run.descentInPoints -= style.rise;
38413942
} else {
38423943
TEXTMETRIC lptm = new TEXTMETRIC();
3843-
OS.GetTextMetrics(hdc, lptm);
3944+
metricsAdapter.GetTextMetrics(hdc, lptm);
38443945
run.ascentInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmAscent);
38453946
run.descentInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmDescent);
38463947
run.leadingInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmInternalLeading);

0 commit comments

Comments
 (0)