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
);
167 _In_opt_ PVOID pReserved
)
169 UNREFERENCED_PARAMETER(pReserved
);
173 case DLL_PROCESS_ATTACH
:
174 DisableThreadLibraryCalls(hDll
);
175 /* Tell usp10 it is activated usp10 */
177 LPK_ApplyMirroring();
204 LPWORD glyphs
= NULL
;
205 LPWSTR reordered_str
= NULL
;
207 DWORD dwSICFlags
= SIC_COMPLEX
;
208 BOOL bResult
, bReorder
;
210 UNREFERENCED_PARAMETER(unknown
);
212 fuOptions
|= ETO_IGNORELANGUAGE
;
214 /* Check text direction */
215 if ((GetLayout(hdc
) & LAYOUT_RTL
) || (GetTextAlign(hdc
) & TA_RTLREADING
))
216 fuOptions
|= ETO_RTLREADING
;
218 /* If text direction is RTL change flag to account neutral characters */
219 if (fuOptions
& ETO_RTLREADING
)
220 dwSICFlags
|= SIC_NEUTRAL
;
222 /* Check if the string requires complex script processing and not a "glyph indices" array */
223 if (ScriptIsComplex(lpString
, uCount
, dwSICFlags
) == S_OK
&& !(fuOptions
& ETO_GLYPH_INDEX
))
225 /* reordered_str is used as fallback in case the glyphs array fails to generate,
226 BIDI_Reorder() doesn't attempt to write into reordered_str if memory allocation fails */
227 reordered_str
= HeapAlloc(GetProcessHeap(), 0, uCount
* sizeof(WCHAR
));
229 bReorder
= BIDI_Reorder(hdc
, lpString
, uCount
, GCP_REORDER
,
230 (fuOptions
& ETO_RTLREADING
) ? WINE_GCPW_FORCE_RTL
: WINE_GCPW_FORCE_LTR
,
231 reordered_str
, uCount
, NULL
, &glyphs
, &cGlyphs
);
233 /* Now display the reordered text if any of the arrays is valid and if BIDI_Reorder() succeeded */
234 if ((glyphs
|| reordered_str
) && bReorder
)
238 fuOptions
|= ETO_GLYPH_INDEX
;
242 bResult
= ExtTextOutW(hdc
, x
, y
, fuOptions
, lprc
,
243 glyphs
? (LPWSTR
)glyphs
: reordered_str
, uCount
, lpDx
);
247 WARN("BIDI_Reorder failed, falling back to original string.\n");
248 bResult
= ExtTextOutW(hdc
, x
, y
, fuOptions
, lprc
, lpString
, uCount
, lpDx
);
251 HeapFree(GetProcessHeap(), 0, glyphs
);
252 HeapFree(GetProcessHeap(), 0, reordered_str
);
257 return ExtTextOutW(hdc
, x
, y
, fuOptions
, lprc
, lpString
, uCount
, lpDx
);
265 LpkGetCharacterPlacement(
270 LPGCP_RESULTSW lpResults
,
276 SCRIPT_STRING_ANALYSIS ssa
;
277 LPWORD lpGlyphs
= NULL
;
282 UNREFERENCED_PARAMETER(dwUnused
);
284 /* Sanity check (most likely a direct call) */
285 if (!(dwFlags
& GCP_REORDER
))
286 return GetCharacterPlacementW(hdc
, lpString
, uCount
, nMaxExtent
, lpResults
, dwFlags
);
289 if (nSet
> lpResults
->nGlyphs
)
290 nSet
= lpResults
->nGlyphs
;
292 BIDI_Reorder(hdc
, lpString
, uCount
, dwFlags
, WINE_GCPW_FORCE_LTR
, lpResults
->lpOutString
,
293 nSet
, lpResults
->lpOrder
, &lpGlyphs
, &cGlyphs
);
295 lpResults
->nGlyphs
= (UINT
)cGlyphs
;
297 if (lpResults
->lpGlyphs
)
300 StringCchCopyW(lpResults
->lpGlyphs
, cGlyphs
, lpGlyphs
);
301 else if (lpResults
->lpOutString
)
302 GetGlyphIndicesW(hdc
, lpResults
->lpOutString
, nSet
, lpResults
->lpGlyphs
, 0);
309 /* If glyph shaping was requested */
310 if (dwFlags
& GCP_GLYPHSHAPE
)
312 if (lpResults
->lpGlyphs
)
314 for (i
= 0; i
< lpResults
->nGlyphs
; i
++)
316 if (GetCharWidthI(hdc
, 0, 1, (WORD
*)&lpResults
->lpGlyphs
[i
], &c
))
317 lpResults
->lpDx
[i
] = c
;
324 for (i
= 0; i
< nSet
; i
++)
326 if (GetCharWidth32W(hdc
, lpResults
->lpOutString
[i
], lpResults
->lpOutString
[i
], &c
))
327 lpResults
->lpDx
[i
] = c
;
332 if (lpResults
->lpCaretPos
)
336 hr
= ScriptStringAnalyse(hdc
, lpString
, nSet
, (3 * nSet
/ 2 + 16), -1, SSA_GLYPHS
, -1,
337 NULL
, NULL
, NULL
, NULL
, NULL
, &ssa
);
340 for (i
= 0; i
< nSet
; i
++)
342 if (ScriptStringCPtoX(ssa
, i
, FALSE
, &pos
) == S_OK
)
343 lpResults
->lpCaretPos
[i
] = pos
;
345 ScriptStringFree(&ssa
);
349 lpResults
->lpCaretPos
[0] = 0;
350 for (i
= 1; i
< nSet
; i
++)
352 if (GetTextExtentPoint32W(hdc
, &(lpString
[i
- 1]), 1, &size
))
353 lpResults
->lpCaretPos
[i
] = (pos
+= size
.cx
);
358 if (GetTextExtentPoint32W(hdc
, lpString
, uCount
, &size
))
359 ret
= MAKELONG(size
.cx
, size
.cy
);
361 HeapFree(GetProcessHeap(), 0, lpGlyphs
);
366 /* Stripped down version of DrawText(), can only draw single line text and Prefix underscore
367 * (only on the last found amperstand).
368 * Only flags to be found to be of use in testing:
370 * DT_NOPREFIX - Draw the string as is without removal of the amperstands and without underscore
371 * DT_HIDEPREFIX - Draw the string without underscore
372 * DT_PREFIXONLY - Draw only the underscore
374 * Without any of these flags the behavior is the string being drawn without the amperstands and
375 * with the underscore.
376 * user32 has an equivalent function - UserLpkPSMTextOut().
378 * Note: lpString does not need to be null terminated.
380 INT WINAPI
LpkPSMTextOut(HDC hdc
, int x
, int y
, LPCWSTR lpString
, int cString
, DWORD dwFlags
)
384 int prefix_offset
, len
;
385 LPWSTR display_str
= NULL
;
387 if (!lpString
|| cString
<= 0)
390 if (dwFlags
& DT_NOPREFIX
)
392 LpkExtTextOut(hdc
, x
, y
, 0, NULL
, lpString
, cString
, NULL
, 0);
393 GetTextExtentPointW(hdc
, lpString
, cString
, &size
);
397 display_str
= HeapAlloc(GetProcessHeap(), 0, (cString
+ 1) * sizeof(WCHAR
));
402 PSM_PrepareToDraw(lpString
, cString
, display_str
, &len
);
404 if (!(dwFlags
& DT_PREFIXONLY
))
405 LpkExtTextOut(hdc
, x
, y
, 0, NULL
, display_str
, len
, NULL
, 0);
407 if (!(dwFlags
& DT_HIDEPREFIX
))
409 prefix_offset
= PSM_FindLastPrefix(lpString
, cString
);
410 GetTextMetricsW(hdc
, &tm
);
411 LPK_DrawUnderscore(hdc
, x
, y
+ tm
.tmAscent
+ 1, display_str
, len
, prefix_offset
);
414 GetTextExtentPointW(hdc
, display_str
, len
+ 1, &size
);
415 HeapFree(GetProcessHeap(), 0, display_str
);
425 LpkGetTextExtentExPoint(
436 SCRIPT_STRING_ANALYSIS ssa
;
441 UNREFERENCED_PARAMETER(dwUnused
);
442 UNREFERENCED_PARAMETER(unknown
);
444 if (cString
< 0 || !lpSize
)
447 if (cString
== 0 || !lpString
)
454 /* Check if any processing is required */
455 if (ScriptIsComplex(lpString
, cString
, SIC_COMPLEX
) != S_OK
)
458 hr
= ScriptStringAnalyse(hdc
, lpString
, cString
, 3 * cString
/ 2 + 16, -1,
459 SSA_GLYPHS
, 0, NULL
, NULL
, NULL
, NULL
, NULL
, &ssa
);
463 /* Use logic from TextIntGetTextExtentPoint() */
464 Dx
= HeapAlloc(GetProcessHeap(), 0, cString
* sizeof(INT
));
467 ScriptStringFree(&ssa
);
474 ScriptStringGetLogicalWidths(ssa
, Dx
);
476 for (i
= 0, extent
= 0; i
< cString
; i
++)
480 if (extent
<= nMaxExtent
&& lpnFit
)
487 HeapFree(GetProcessHeap(), 0, Dx
);
488 ScriptStringFree(&ssa
);
490 if (!GetTextMetricsW(hdc
, &tm
))
491 return GetTextExtentExPointWPri(hdc
, lpString
, cString
, 0, NULL
, NULL
, lpSize
);
494 lpSize
->cy
= tm
.tmHeight
;
499 return GetTextExtentExPointWPri(hdc
, lpString
, cString
, nMaxExtent
, lpnFit
, lpnDx
, lpSize
);