3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Language Pack DLL.
5 * PROGRAMMERS: Magnus Olsen (greatlrd)
6 * Baruch Rutman (peterooch at gmail dot com)
11 WINE_DEFAULT_DEBUG_CHANNEL(bidi
);
13 LPK_LPEDITCONTROL_LIST LpkEditControl
= {EditCreate
, EditIchToXY
, EditMouseToIch
, EditCchInWidth
,
14 EditGetLineWidth
, EditDrawText
, EditHScroll
, EditMoveSelection
,
15 EditVerifyText
, EditNextWord
, EditSetMenu
, EditProcessMenu
,
16 EditCreateCaret
, EditAdjustCaret
};
19 #define ALPHA_PREFIX 30 /* Win16: Alphabet prefix */
20 #define KANA_PREFIX 31 /* Win16: Katakana prefix */
22 static int PSM_FindLastPrefix(LPCWSTR str
, int count
)
24 int i
, prefix_count
= 0, index
= -1;
26 for (i
= 0; i
< count
- 1; i
++)
28 if (str
[i
] == PREFIX
&& str
[i
+ 1] != PREFIX
)
30 index
= i
- prefix_count
;
33 else if (str
[i
] == PREFIX
&& str
[i
+ 1] == PREFIX
)
41 static void PSM_PrepareToDraw(LPCWSTR str
, INT count
, LPWSTR new_str
, LPINT new_count
)
43 int len
, i
= 0, j
= 0;
47 if (str
[i
] == PREFIX
|| (iswspace(str
[i
]) && str
[i
] != L
' '))
49 if (i
< count
- 1 && str
[i
+ 1] == PREFIX
)
50 new_str
[j
++] = str
[i
++];
56 new_str
[j
++] = str
[i
++];
61 len
= wcslen(new_str
);
65 /* Can be used with also LpkDrawTextEx if it will be implemented */
66 static void LPK_DrawUnderscore(HDC hdc
, int x
, int y
, LPCWSTR str
, int count
, int offset
)
68 SCRIPT_STRING_ANALYSIS ssa
;
69 DWORD dwSSAFlags
= SSA_GLYPHS
;
81 if (ScriptIsComplex(str
, count
, SIC_COMPLEX
) == S_OK
)
83 if (GetLayout(hdc
) & LAYOUT_RTL
|| GetTextAlign(hdc
) & TA_RTLREADING
)
84 dwSSAFlags
|= SSA_RTL
;
86 hr
= ScriptStringAnalyse(hdc
, str
, count
, (3 * count
/ 2 + 16),
87 -1, dwSSAFlags
, -1, NULL
, NULL
, NULL
, NULL
, NULL
, &ssa
);
92 ScriptStringCPtoX(ssa
, offset
, FALSE
, &pos
);
94 ScriptStringCPtoX(ssa
, offset
, TRUE
, &pos
);
96 ScriptStringFree(&ssa
);
100 GetTextExtentPointW(hdc
, str
, offset
, &size
);
101 prefix_x
= x
+ size
.cx
;
102 GetTextExtentPointW(hdc
, str
, offset
+ 1, &size
);
103 prefix_end
= x
+ size
.cx
- 1;
105 hpen
= CreatePen(PS_SOLID
, 1, GetTextColor(hdc
));
106 oldPen
= SelectObject(hdc
, hpen
);
107 MoveToEx(hdc
, prefix_x
, y
, NULL
);
108 LineTo(hdc
, prefix_end
, y
);
109 SelectObject(hdc
, oldPen
);
113 /* Code taken from the GetProcessDefaultLayout function from Wine's user32
116 * This function should be called from LpkInitialize,
117 * which is in turn called by GdiInitializeLanguagePack (from gdi32).
118 * TODO: Move call from LpkDllInitialize to LpkInitialize when latter
119 * function is implemented.
121 static void LPK_ApplyMirroring()
123 static const WCHAR translationW
[] = { '\\','V','a','r','F','i','l','e','I','n','f','o',
124 '\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
125 static const WCHAR filedescW
[] = { '\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o',
126 '\\','%','0','4','x','%','0','4','x',
127 '\\','F','i','l','e','D','e','s','c','r','i','p','t','i','o','n',0 };
128 WCHAR
*str
, buffer
[MAX_PATH
];
130 DWORD i
, version_layout
= 0;
133 DWORD i
, len
, version_layout
= 0;
135 DWORD user_lang
= GetUserDefaultLangID();
139 GetModuleFileNameW( 0, buffer
, MAX_PATH
);
140 if (!(len
= GetFileVersionInfoSizeW( buffer
, NULL
))) goto done
;
141 if (!(data
= HeapAlloc( GetProcessHeap(), 0, len
))) goto done
;
142 if (!GetFileVersionInfoW( buffer
, 0, len
, data
)) goto done
;
143 if (!VerQueryValueW( data
, translationW
, (void **)&languages
, &len
) || !len
) goto done
;
145 len
/= sizeof(DWORD
);
146 for (i
= 0; i
< len
; i
++) if (LOWORD(languages
[i
]) == user_lang
) break;
147 if (i
== len
) /* try neutral language */
148 for (i
= 0; i
< len
; i
++)
149 if (LOWORD(languages
[i
]) == MAKELANGID( PRIMARYLANGID(user_lang
), SUBLANG_NEUTRAL
)) break;
150 if (i
== len
) i
= 0; /* default to the first one */
152 sprintfW( buffer
, filedescW
, LOWORD(languages
[i
]), HIWORD(languages
[i
]) );
153 if (!VerQueryValueW( data
, buffer
, (void **)&str
, &len
)) goto done
;
154 TRACE( "found description %s\n", debugstr_w( str
));
155 if (str
[0] == 0x200e && str
[1] == 0x200e) version_layout
= LAYOUT_RTL
;
158 HeapFree( GetProcessHeap(), 0, data
);
159 SetProcessDefaultLayout(version_layout
);
170 return LpkDllInitialize(hDll
,dwReason
,lpReserved
);
182 case DLL_PROCESS_ATTACH
:
183 DisableThreadLibraryCalls(hDll
);
184 /* Tell usp10 it is activated usp10 */
186 LPK_ApplyMirroring();
213 LPWORD glyphs
= NULL
;
214 LPWSTR reordered_str
= NULL
;
216 DWORD dwSICFlags
= SIC_COMPLEX
;
217 BOOL bResult
, bReorder
;
219 UNREFERENCED_PARAMETER(unknown
);
221 fuOptions
|= ETO_IGNORELANGUAGE
;
223 /* Check text direction */
224 if ((GetLayout(hdc
) & LAYOUT_RTL
) || (GetTextAlign(hdc
) & TA_RTLREADING
))
225 fuOptions
|= ETO_RTLREADING
;
227 /* If text direction is RTL change flag to account neutral characters */
228 if (fuOptions
& ETO_RTLREADING
)
229 dwSICFlags
|= SIC_NEUTRAL
;
231 /* Check if the string requires complex script processing and not a "glyph indices" array */
232 if (ScriptIsComplex(lpString
, uCount
, dwSICFlags
) == S_OK
&& !(fuOptions
& ETO_GLYPH_INDEX
))
234 /* reordered_str is used as fallback in case the glyphs array fails to generate,
235 BIDI_Reorder doesn't attempt to write into reordered_str if memory allocation fails */
236 reordered_str
= HeapAlloc(GetProcessHeap(), 0, uCount
* sizeof(WCHAR
));
238 bReorder
= BIDI_Reorder(hdc
, lpString
, uCount
, GCP_REORDER
,
239 (fuOptions
& ETO_RTLREADING
) ? WINE_GCPW_FORCE_RTL
: WINE_GCPW_FORCE_LTR
,
240 reordered_str
, uCount
, NULL
, &glyphs
, &cGlyphs
);
242 /* Now display the reordered text if any of the arrays is valid and if BIDI_Reorder succeeded */
243 if ((glyphs
|| reordered_str
) && bReorder
)
247 fuOptions
|= ETO_GLYPH_INDEX
;
251 bResult
= ExtTextOutW(hdc
, x
, y
, fuOptions
, lprc
,
252 glyphs
? (LPWSTR
)glyphs
: reordered_str
, uCount
, lpDx
);
256 WARN("BIDI_Reorder failed, falling back to original string.\n");
257 bResult
= ExtTextOutW(hdc
, x
, y
, fuOptions
, lprc
, lpString
, uCount
, lpDx
);
260 HeapFree(GetProcessHeap(), 0, glyphs
);
261 HeapFree(GetProcessHeap(), 0, reordered_str
);
266 return ExtTextOutW(hdc
, x
, y
, fuOptions
, lprc
, lpString
, uCount
, lpDx
);
274 LpkGetCharacterPlacement(
279 LPGCP_RESULTSW lpResults
,
285 SCRIPT_STRING_ANALYSIS ssa
;
286 LPWORD lpGlyphs
= NULL
;
291 UNREFERENCED_PARAMETER(dwUnused
);
293 /* Sanity check (most likely a direct call) */
294 if (!(dwFlags
& GCP_REORDER
))
295 return GetCharacterPlacementW(hdc
, lpString
, uCount
, nMaxExtent
, lpResults
, dwFlags
);
298 if (nSet
> lpResults
->nGlyphs
)
299 nSet
= lpResults
->nGlyphs
;
301 BIDI_Reorder(hdc
, lpString
, uCount
, dwFlags
, WINE_GCPW_FORCE_LTR
, lpResults
->lpOutString
,
302 nSet
, lpResults
->lpOrder
, &lpGlyphs
, &cGlyphs
);
304 lpResults
->nGlyphs
= (UINT
)cGlyphs
;
306 if (lpResults
->lpGlyphs
)
309 StringCchCopyW(lpResults
->lpGlyphs
, cGlyphs
, lpGlyphs
);
310 else if (lpResults
->lpOutString
)
311 GetGlyphIndicesW(hdc
, lpResults
->lpOutString
, nSet
, lpResults
->lpGlyphs
, 0);
318 /* If glyph shaping was requested */
319 if (dwFlags
& GCP_GLYPHSHAPE
)
321 if (lpResults
->lpGlyphs
)
323 for (i
= 0; i
< lpResults
->nGlyphs
; i
++)
325 if (GetCharWidthI(hdc
, 0, 1, (WORD
*)&lpResults
->lpGlyphs
[i
], &c
))
326 lpResults
->lpDx
[i
] = c
;
333 for (i
= 0; i
< nSet
; i
++)
335 if (GetCharWidth32W(hdc
, lpResults
->lpOutString
[i
], lpResults
->lpOutString
[i
], &c
))
336 lpResults
->lpDx
[i
] = c
;
341 if (lpResults
->lpCaretPos
)
345 hr
= ScriptStringAnalyse(hdc
, lpString
, nSet
, (3 * nSet
/ 2 + 16), -1, SSA_GLYPHS
, -1,
346 NULL
, NULL
, NULL
, NULL
, NULL
, &ssa
);
349 for (i
= 0; i
< nSet
; i
++)
351 if (ScriptStringCPtoX(ssa
, i
, FALSE
, &pos
) == S_OK
)
352 lpResults
->lpCaretPos
[i
] = pos
;
354 ScriptStringFree(&ssa
);
358 lpResults
->lpCaretPos
[0] = 0;
359 for (i
= 1; i
< nSet
; i
++)
361 if (GetTextExtentPoint32W(hdc
, &(lpString
[i
- 1]), 1, &size
))
362 lpResults
->lpCaretPos
[i
] = (pos
+= size
.cx
);
367 if (GetTextExtentPoint32W(hdc
, lpString
, uCount
, &size
))
368 ret
= MAKELONG(size
.cx
, size
.cy
);
370 HeapFree(GetProcessHeap(), 0, lpGlyphs
);
375 /* Stripped down version of DrawText, can only draw single line text and Prefix underscore
376 * (only on the last found amperstand)
377 * only flags to be found to be of use in testing:
379 * DT_NOPREFIX - Draw the string as is without removal of the amperstands and without underscore
380 * DT_HIDEPREFIX - Draw the string without underscore
381 * DT_PREFIXONLY - Draw only the underscore
383 * without any of these flags the behavior is the string being drawn without the amperstands and
384 * with the underscore.
385 * user32 has an equivalent function - UserLpkPSMTextOut
387 * Note: lpString does not need to be null terminated
389 INT WINAPI
LpkPSMTextOut(HDC hdc
, int x
, int y
, LPCWSTR lpString
, int cString
, DWORD dwFlags
)
393 int prefix_offset
, len
;
394 LPWSTR display_str
= NULL
;
396 if (!lpString
|| cString
<= 0)
399 if (dwFlags
& DT_NOPREFIX
)
401 LpkExtTextOut(hdc
, x
, y
, 0, NULL
, lpString
, cString
, NULL
, 0);
402 GetTextExtentPointW(hdc
, lpString
, cString
, &size
);
406 display_str
= HeapAlloc(GetProcessHeap(), 0, (cString
+ 1) * sizeof(WCHAR
));
411 PSM_PrepareToDraw(lpString
, cString
, display_str
, &len
);
413 if (!(dwFlags
& DT_PREFIXONLY
))
414 LpkExtTextOut(hdc
, x
, y
, 0, NULL
, display_str
, len
, NULL
, 0);
416 if (!(dwFlags
& DT_HIDEPREFIX
))
418 prefix_offset
= PSM_FindLastPrefix(lpString
, cString
);
419 GetTextMetricsW(hdc
, &tm
);
420 LPK_DrawUnderscore(hdc
, x
, y
+ tm
.tmAscent
+ 1, display_str
, len
, prefix_offset
);
423 GetTextExtentPointW(hdc
, display_str
, len
+ 1, &size
);
424 HeapFree(GetProcessHeap(), 0, display_str
);
434 LpkGetTextExtentExPoint(
445 SCRIPT_STRING_ANALYSIS ssa
;
450 UNREFERENCED_PARAMETER(dwUnused
);
451 UNREFERENCED_PARAMETER(unknown
);
453 if (cString
< 0 || !lpSize
)
463 /* Check if any processing is required */
464 if (ScriptIsComplex(lpString
, cString
, SIC_COMPLEX
) != S_OK
)
465 return GetTextExtentExPointWPri(hdc
, lpString
, cString
, nMaxExtent
, lpnFit
, lpnDx
, lpSize
);
467 hr
= ScriptStringAnalyse(hdc
, lpString
, cString
, 3 * cString
/ 2 + 16, -1,
468 SSA_GLYPHS
, 0, NULL
, NULL
, NULL
, NULL
, NULL
, &ssa
);
473 pSize
= ScriptString_pSize(ssa
);
478 GetTextExtentExPointWPri(hdc
, lpString
, cString
, 0, NULL
, NULL
, lpSize
);
480 /* Use logic from TextIntGetTextExtentPoint */
483 Dx
= HeapAlloc(GetProcessHeap(), 0, cString
* sizeof(INT
));
487 ScriptStringFree(&ssa
);
494 ScriptStringGetLogicalWidths(ssa
, Dx
);
496 for (i
= 0, extent
= 0; i
< cString
; i
++)
500 if (extent
<= nMaxExtent
&& lpnFit
)
507 HeapFree(GetProcessHeap(), 0, Dx
);
510 ScriptStringFree(&ssa
);