[LPK] Implement LpkPSMTextOut(). (#890)
[reactos.git] / dll / win32 / lpk / lpk.c
1 /*
2 * PROJECT: ReactOS LPK
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)
7 */
8
9 #include "ros_lpk.h"
10 #include <debug.h>
11
12 LPK_LPEDITCONTROL_LIST LpkEditControl = {EditCreate, EditIchToXY, EditMouseToIch, EditCchInWidth,
13 EditGetLineWidth, EditDrawText, EditHScroll, EditMoveSelection,
14 EditVerifyText, EditNextWord, EditSetMenu, EditProcessMenu,
15 EditCreateCaret, EditAdjustCaret};
16
17 #define PREFIX 38
18 #define ALPHA_PREFIX 30 /* Win16: Alphabet prefix */
19 #define KANA_PREFIX 31 /* Win16: Katakana prefix */
20
21 static int PSM_FindLastPrefix(LPCWSTR str, int count)
22 {
23 int i, prefix_count = 0, index = -1;
24
25 for (i = 0; i < count - 1; i++)
26 {
27 if (str[i] == PREFIX && str[i + 1] != PREFIX)
28 {
29 index = i - prefix_count;
30 prefix_count++;
31 }
32 else if (str[i] == PREFIX && str[i + 1] == PREFIX)
33 {
34 i++;
35 }
36 }
37 return index;
38 }
39
40 static void PSM_PrepareToDraw(LPCWSTR str, INT count, LPWSTR new_str, LPINT new_count)
41 {
42 int len, i = 0, j = 0;
43
44 while (i < count)
45 {
46 if (str[i] == PREFIX || (iswspace(str[i]) && str[i] != L' '))
47 {
48 if(i < count - 1 && str[i + 1] == PREFIX)
49 new_str[j++] = str[i++];
50 else
51 i++;
52 }
53 else
54 {
55 new_str[j++] = str[i++];
56 }
57 }
58
59 new_str[j] = L'\0';
60 len = wcslen(new_str);
61 *new_count = len;
62 }
63
64 /* Can be used with also LpkDrawTextEx if it will be implemented */
65 static void LPK_DrawUnderscore(HDC hdc, int x, int y, LPCWSTR str, int count, int offset)
66 {
67 SCRIPT_STRING_ANALYSIS ssa;
68 int prefix_x;
69 int prefix_end;
70 int pos;
71 SIZE size;
72 HPEN hpen;
73 HPEN oldPen;
74 HRESULT hr = S_FALSE;
75
76 if (offset == -1)
77 return;
78
79 if (ScriptIsComplex(str, count, SIC_COMPLEX) == S_OK)
80 {
81 hr = ScriptStringAnalyse(hdc, str, count, (3 * count / 2 + 16),
82 -1, SSA_GLYPHS, -1, NULL, NULL, NULL, NULL, NULL, &ssa);
83 }
84
85 if (hr == S_OK)
86 {
87 ScriptStringCPtoX(ssa, offset, FALSE, &pos);
88 prefix_x = x + pos;
89 ScriptStringCPtoX(ssa, offset, TRUE, &pos);
90 prefix_end = x + pos;
91 ScriptStringFree(&ssa);
92 }
93 else
94 {
95 GetTextExtentPointW(hdc, str, offset, &size);
96 prefix_x = x + size.cx;
97 GetTextExtentPointW(hdc, str, offset + 1, &size);
98 prefix_end = x + size.cx - 1;
99 }
100 hpen = CreatePen(PS_SOLID, 1, GetTextColor(hdc));
101 oldPen = SelectObject(hdc, hpen);
102 MoveToEx(hdc, prefix_x, y, NULL);
103 LineTo(hdc, prefix_end, y);
104 SelectObject(hdc, oldPen);
105 DeleteObject(hpen);
106 }
107
108 BOOL
109 WINAPI
110 DllMain(
111 HANDLE hDll,
112 DWORD dwReason,
113 LPVOID lpReserved)
114 {
115
116 return LpkDllInitialize(hDll,dwReason,lpReserved);
117 }
118
119 BOOL
120 WINAPI
121 LpkDllInitialize(
122 HANDLE hDll,
123 DWORD dwReason,
124 LPVOID lpReserved)
125 {
126 switch(dwReason)
127 {
128 case DLL_PROCESS_ATTACH:
129 DisableThreadLibraryCalls(hDll);
130 /* Tell usp10 it is activated usp10 */
131 //LpkPresent();
132 break;
133
134 default:
135 break;
136 }
137
138 return TRUE;
139 }
140
141
142 /*
143 * @implemented
144 */
145 BOOL
146 WINAPI
147 LpkExtTextOut(
148 HDC hdc,
149 int x,
150 int y,
151 UINT fuOptions,
152 const RECT *lprc,
153 LPCWSTR lpString,
154 UINT uCount,
155 const INT *lpDx,
156 INT unknown)
157 {
158 LPWORD glyphs = NULL;
159 LPWSTR reordered_str = NULL;
160 INT cGlyphs;
161 DWORD dwSICFlags = SIC_COMPLEX;
162 BOOL bResult, bReorder;
163
164 UNREFERENCED_PARAMETER(unknown);
165
166 fuOptions |= ETO_IGNORELANGUAGE;
167
168 /* Check text direction */
169 if ((GetLayout(hdc) & LAYOUT_RTL) || (GetTextAlign(hdc) & TA_RTLREADING))
170 fuOptions |= ETO_RTLREADING;
171
172 /* If text direction is RTL change flag to account neutral characters */
173 if (fuOptions & ETO_RTLREADING)
174 dwSICFlags |= SIC_NEUTRAL;
175
176 /* Check if the string requires complex script processing and not a "glyph indices" array */
177 if (ScriptIsComplex(lpString, uCount, dwSICFlags) == S_OK && !(fuOptions & ETO_GLYPH_INDEX))
178 {
179 /* reordered_str is used as fallback in case the glyphs array fails to generate,
180 BIDI_Reorder doesn't attempt to write into reordered_str if memory allocation fails */
181 reordered_str = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(WCHAR));
182
183 bReorder = BIDI_Reorder(hdc, lpString, uCount, GCP_REORDER,
184 (fuOptions & ETO_RTLREADING) ? WINE_GCPW_FORCE_RTL : WINE_GCPW_FORCE_LTR,
185 reordered_str, uCount, NULL, &glyphs, &cGlyphs);
186
187 if (glyphs)
188 {
189 fuOptions |= ETO_GLYPH_INDEX;
190 uCount = cGlyphs;
191 }
192
193 /* Now display the reordered text if any of the arrays is valid and if BIDI_Reorder succeeded */
194 if ((glyphs || reordered_str) && bReorder)
195 {
196 bResult = ExtTextOutW(hdc, x, y, fuOptions, lprc,
197 glyphs ? (LPWSTR)glyphs : reordered_str, uCount, lpDx);
198 }
199 else
200 {
201 DPRINT1("BIDI_Reorder failed, falling back to original string.\n");
202 bResult = ExtTextOutW(hdc, x, y, fuOptions, lprc, lpString, uCount, lpDx);
203 }
204
205 HeapFree(GetProcessHeap(), 0, glyphs);
206 HeapFree(GetProcessHeap(), 0, reordered_str);
207
208 return bResult;
209 }
210
211 return ExtTextOutW(hdc, x, y, fuOptions, lprc, lpString, uCount, lpDx);
212 }
213
214 /*
215 * @implemented
216 */
217 DWORD
218 WINAPI
219 LpkGetCharacterPlacement(
220 HDC hdc,
221 LPCWSTR lpString,
222 INT uCount,
223 INT nMaxExtent,
224 LPGCP_RESULTSW lpResults,
225 DWORD dwFlags,
226 DWORD dwUnused)
227 {
228 DWORD ret = 0;
229 HRESULT hr;
230 SCRIPT_STRING_ANALYSIS ssa;
231 LPWORD lpGlyphs = NULL;
232 SIZE size;
233 UINT nSet, i;
234 INT cGlyphs;
235
236 UNREFERENCED_PARAMETER(dwUnused);
237
238 /* Sanity check (most likely a direct call) */
239 if (!(dwFlags & GCP_REORDER))
240 return GetCharacterPlacementW(hdc, lpString, uCount, nMaxExtent, lpResults, dwFlags);
241
242 nSet = (UINT)uCount;
243 if (nSet > lpResults->nGlyphs)
244 nSet = lpResults->nGlyphs;
245
246 BIDI_Reorder(hdc, lpString, uCount, dwFlags, WINE_GCPW_FORCE_LTR, lpResults->lpOutString,
247 nSet, lpResults->lpOrder, &lpGlyphs, &cGlyphs);
248
249 lpResults->nGlyphs = (UINT)cGlyphs;
250
251 if (lpResults->lpGlyphs)
252 {
253 if (lpGlyphs)
254 StringCchCopyW(lpResults->lpGlyphs, cGlyphs, lpGlyphs);
255 else if (lpResults->lpOutString)
256 GetGlyphIndicesW(hdc, lpResults->lpOutString, nSet, lpResults->lpGlyphs, 0);
257 }
258
259 if (lpResults->lpDx)
260 {
261 int c;
262
263 /* If glyph shaping was requested */
264 if (dwFlags & GCP_GLYPHSHAPE)
265 {
266 if (lpResults->lpGlyphs)
267 {
268 for (i = 0; i < lpResults->nGlyphs; i++)
269 {
270 if (GetCharWidthI(hdc, 0, 1, (WORD *)&lpResults->lpGlyphs[i], &c))
271 lpResults->lpDx[i] = c;
272 }
273 }
274 }
275
276 else
277 {
278 for (i = 0; i < nSet; i++)
279 {
280 if (GetCharWidth32W(hdc, lpResults->lpOutString[i], lpResults->lpOutString[i], &c))
281 lpResults->lpDx[i] = c;
282 }
283 }
284 }
285
286 if (lpResults->lpCaretPos)
287 {
288 int pos = 0;
289
290 hr = ScriptStringAnalyse(hdc, lpString, nSet, (3 * nSet / 2 + 16), -1, SSA_GLYPHS, -1,
291 NULL, NULL, NULL, NULL, NULL, &ssa);
292 if (hr == S_OK)
293 {
294 for (i = 0; i < nSet; i++)
295 {
296 if (ScriptStringCPtoX(ssa, i, FALSE, &pos) == S_OK)
297 lpResults->lpCaretPos[i] = pos;
298 }
299 ScriptStringFree(&ssa);
300 }
301 else
302 {
303 lpResults->lpCaretPos[0] = 0;
304 for (i = 1; i < nSet; i++)
305 {
306 if (GetTextExtentPoint32W(hdc, &(lpString[i - 1]), 1, &size))
307 lpResults->lpCaretPos[i] = (pos += size.cx);
308 }
309 }
310 }
311
312 if (GetTextExtentPoint32W(hdc, lpString, uCount, &size))
313 ret = MAKELONG(size.cx, size.cy);
314
315 HeapFree(GetProcessHeap(), 0, lpGlyphs);
316
317 return ret;
318 }
319
320 /* Stripped down version of DrawText, can only draw single line text and Prefix underscore
321 * (only on the last found amperstand)
322 * only flags to be found to be of use in testing:
323 *
324 * DT_NOPREFIX - Draw the string as is without removal of the amperstands and without underscore
325 * DT_HIDEPREFIX - Draw the string without underscore
326 * DT_PREFIXONLY - Draw only the underscore
327 *
328 * without any of these flags the behavior is the string being drawn without the amperstands and
329 * with the underscore.
330 * user32 has an equivalent function - UserLpkPSMTextOut
331 */
332 INT WINAPI LpkPSMTextOut(HDC hdc, int x, int y, LPCWSTR lpString, int cString, DWORD dwFlags)
333 {
334 SIZE size;
335 TEXTMETRICW tm;
336 int prefix_offset, len;
337 LPWSTR display_str = NULL;
338
339 if (!lpString || cString <= 0)
340 return 0;
341
342 if (dwFlags & DT_NOPREFIX)
343 {
344 LpkExtTextOut(hdc, x, y, (dwFlags & DT_RTLREADING) ? ETO_RTLREADING : 0, NULL, lpString, cString - 1, NULL, 0);
345 GetTextExtentPointW(hdc, lpString, cString, &size);
346 return size.cx;
347 }
348
349 display_str = HeapAlloc(GetProcessHeap(), 0, (cString + 1) * sizeof(WCHAR));
350
351 if (!display_str)
352 return 0;
353
354 PSM_PrepareToDraw(lpString, cString, display_str, &len);
355
356 if (!(dwFlags & DT_PREFIXONLY))
357 LpkExtTextOut(hdc, x, y, (dwFlags & DT_RTLREADING) ? ETO_RTLREADING : 0, NULL, display_str, len, NULL, 0);
358
359 if (!(dwFlags & DT_HIDEPREFIX))
360 {
361 prefix_offset = PSM_FindLastPrefix(lpString, cString);
362 GetTextMetricsW(hdc, &tm);
363 LPK_DrawUnderscore(hdc, x, y + tm.tmAscent + 1, display_str, len, prefix_offset);
364 }
365
366 GetTextExtentPointW(hdc, display_str, len + 1, &size);
367 HeapFree(GetProcessHeap(), 0, display_str);
368
369 return size.cx;
370 }