@@ -64,6 +64,8 @@ public final class TextLayout extends Resource {
64
64
IMLangFontLink2 mLangFontLink2 ;
65
65
int verticalIndentInPoints ;
66
66
67
+ private MetricsAdapter metricsAdapter = new MetricsAdapter ();
68
+
67
69
static final char LTR_MARK = '\u200E' , RTL_MARK = '\u200F' ;
68
70
static final int SCRIPT_VISATTR_SIZEOF = 2 ;
69
71
static final int GOFFSET_SIZEOF = 8 ;
@@ -185,6 +187,100 @@ public String toString () {
185
187
}
186
188
}
187
189
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
+
188
284
/**
189
285
* Constructs a new instance of this class on the given device.
190
286
* <p>
@@ -440,7 +536,7 @@ void computeRuns (GC gc) {
440
536
if (lineRunCount == 1 && (i == allRuns .length - 1 || !run .softBreak )) {
441
537
TEXTMETRIC lptm = new TEXTMETRIC ();
442
538
OS .SelectObject (srcHdc , getItemFont (run ));
443
- OS .GetTextMetrics (srcHdc , lptm );
539
+ metricsAdapter .GetTextMetrics (srcHdc , lptm );
444
540
run .ascentInPoints = DPIUtil .autoScaleDown (getDevice (), lptm .tmAscent );
445
541
run .descentInPoints = DPIUtil .autoScaleDown (getDevice (), lptm .tmDescent );
446
542
ascentInPoints = Math .max (ascentInPoints , run .ascentInPoints );
@@ -1067,11 +1163,11 @@ RECT drawRunText(long hdc, StyleItem run, RECT rect, int baselineInPixels, int c
1067
1163
}
1068
1164
}
1069
1165
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 );
1071
1167
if (partialSelection ) {
1072
1168
getPartialSelection (run , selectionStart , selectionEnd , rect );
1073
1169
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 );
1075
1171
}
1076
1172
return fullSelection || partialSelection ? rect : null ;
1077
1173
}
@@ -1081,8 +1177,14 @@ RECT drawRunTextGDIP(long graphics, StyleItem run, RECT rect, long gdipFont, int
1081
1177
boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1 ;
1082
1178
boolean fullSelection = hasSelection && selectionStart <= run .start && selectionEnd >= end ;
1083
1179
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.
1084
1185
int drawY = rect .top + baselineInPixels ;
1085
1186
if (run .style != null && run .style .rise != 0 ) drawY -= DPIUtil .autoScaleUp (getDevice (), run .style .rise );
1187
+
1086
1188
int drawX = rect .left ;
1087
1189
long brush = color ;
1088
1190
if (fullSelection ) {
@@ -1998,7 +2100,7 @@ public FontMetrics getLineMetrics (int lineIndex) {
1998
2100
long srcHdc = OS .CreateCompatibleDC (hDC );
1999
2101
TEXTMETRIC lptm = new TEXTMETRIC ();
2000
2102
OS .SelectObject (srcHdc , font != null ? font .handle : device .systemFont .handle );
2001
- OS .GetTextMetrics (srcHdc , lptm );
2103
+ metricsAdapter .GetTextMetrics (srcHdc , lptm );
2002
2104
OS .DeleteDC (srcHdc );
2003
2105
device .internal_dispose_GC (hDC , null );
2004
2106
@@ -3108,8 +3210,7 @@ public void setDescent (int descent) {
3108
3210
* @since 3.125
3109
3211
*/
3110
3212
public void setFixedLineMetrics (FontMetrics metrics ) {
3111
- if (metrics == null ) return ;
3112
- SWT .error (SWT .ERROR_NOT_IMPLEMENTED );
3213
+ metricsAdapter .setFixedLineMetrics (metrics );
3113
3214
}
3114
3215
3115
3216
/**
@@ -3798,7 +3899,7 @@ long metaFileEnumProc (long hDC, long table, long record, long nObj, long lpData
3798
3899
OUTLINETEXTMETRIC lotm = null ;
3799
3900
if (style .underline || style .strikeout ) {
3800
3901
lotm = new OUTLINETEXTMETRIC ();
3801
- if (OS .GetOutlineTextMetrics (hdc , OUTLINETEXTMETRIC .sizeof , lotm ) == 0 ) {
3902
+ if (metricsAdapter .GetOutlineTextMetrics (hdc , OUTLINETEXTMETRIC .sizeof , lotm ) == 0 ) {
3802
3903
lotm = null ;
3803
3904
}
3804
3905
}
@@ -3819,7 +3920,7 @@ long metaFileEnumProc (long hDC, long table, long record, long nObj, long lpData
3819
3920
lptm = lotm .otmTextMetrics ;
3820
3921
} else {
3821
3922
lptm = new TEXTMETRIC ();
3822
- OS .GetTextMetrics (hdc , lptm );
3923
+ metricsAdapter .GetTextMetrics (hdc , lptm );
3823
3924
}
3824
3925
run .ascentInPoints = DPIUtil .autoScaleDown (getDevice (), lptm .tmAscent );
3825
3926
run .descentInPoints = DPIUtil .autoScaleDown (getDevice (), lptm .tmDescent );
@@ -3840,7 +3941,7 @@ long metaFileEnumProc (long hDC, long table, long record, long nObj, long lpData
3840
3941
run .descentInPoints -= style .rise ;
3841
3942
} else {
3842
3943
TEXTMETRIC lptm = new TEXTMETRIC ();
3843
- OS .GetTextMetrics (hdc , lptm );
3944
+ metricsAdapter .GetTextMetrics (hdc , lptm );
3844
3945
run .ascentInPoints = DPIUtil .autoScaleDown (getDevice (), lptm .tmAscent );
3845
3946
run .descentInPoints = DPIUtil .autoScaleDown (getDevice (), lptm .tmDescent );
3846
3947
run .leadingInPoints = DPIUtil .autoScaleDown (getDevice (), lptm .tmInternalLeading );
0 commit comments