@@ -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 */
31103212public 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