Commit | Line | Data |
---|---|---|
8457948a | 1 | #define _WIN32_WINNT 0x500 |
718b8c97 | 2 | #include "imw32.h" |
faa9b3e7 TC |
3 | #define STRICT |
4 | #include <windows.h> | |
718b8c97 | 5 | #include "imext.h" |
faa9b3e7 TC |
6 | |
7 | /* | |
8 | =head1 NAME | |
9 | ||
10 | win32.c - implements some win32 specific code, specifically Win32 font support. | |
11 | ||
12 | =head1 SYNOPSIS | |
13 | ||
8d14daab | 14 | i_img_dim bbox[6]; |
faa9b3e7 TC |
15 | if (i_wf_bbox(facename, size, text, text_len, bbox)) { |
16 | // we have the bbox | |
17 | } | |
bd4f550c TC |
18 | i_wf_text(face, im, tx, ty, cl, size, text, len, align, aa, utf8); |
19 | i_wf_cp(face, im, tx, ty, channel, size, text, len, align, aa, utf8) | |
faa9b3e7 | 20 | |
2c68060c | 21 | =head1 DESCRIPTION |
faa9b3e7 | 22 | |
c13a1ea8 | 23 | An Imager interface to font output using the Win32 GDI. |
faa9b3e7 | 24 | |
2c68060c TC |
25 | =over |
26 | ||
27 | =cut | |
28 | */ | |
faa9b3e7 TC |
29 | |
30 | #define fixed(x) ((x).value + ((x).fract) / 65536.0) | |
31 | ||
885e13c5 | 32 | static void set_logfont(const char *face, int size, LOGFONT *lf); |
2c68060c | 33 | |
5091168a TC |
34 | static unsigned char *render_text(const char *face, int size, const char *text, size_t length, int aa, |
35 | SIZE *psz, TEXTMETRIC *tm, size_t *bytes_per_line, i_img_dim *bbox, int utf8); | |
bd4f550c | 36 | static LPWSTR utf8_to_wide_string(char const *text, int text_len, int *wide_chars); |
5091168a | 37 | static LPWSTR latin1_to_wide_string(char const *text, int text_len, int *wide_chars); |
2c68060c | 38 | |
c13a1ea8 | 39 | /* |
bd4f550c | 40 | =item i_wf_bbox(face, size, text, length, bbox, utf8) |
c13a1ea8 TC |
41 | |
42 | Calculate a bounding box for the text. | |
43 | ||
44 | =cut | |
45 | */ | |
46 | ||
8d14daab TC |
47 | int i_wf_bbox(const char *face, i_img_dim size, const char *text, size_t length, |
48 | i_img_dim *bbox, int utf8) { | |
faa9b3e7 TC |
49 | LOGFONT lf; |
50 | HFONT font, oldFont; | |
51 | HDC dc; | |
52 | SIZE sz; | |
53 | TEXTMETRIC tm; | |
54 | ABC first, last; | |
55 | GLYPHMETRICS gm; | |
faa9b3e7 | 56 | MAT2 mat; |
a6d9b737 | 57 | int ascent, descent, max_ascent = -size, min_descent = size; |
bd4f550c | 58 | const char *workp; |
fd0e4176 | 59 | size_t work_len; |
50e800e9 TC |
60 | int got_first_ch = 0; |
61 | unsigned long first_ch, last_ch; | |
faa9b3e7 | 62 | |
8d14daab TC |
63 | i_clear_error(); |
64 | ||
bd4f550c | 65 | mm_log((1, "i_wf_bbox(face %s, size %d, text %p, length %d, bbox %p, utf8 %d)\n", face, size, text, length, bbox, utf8)); |
8457948a | 66 | |
faa9b3e7 TC |
67 | set_logfont(face, size, &lf); |
68 | font = CreateFontIndirect(&lf); | |
69 | if (!font) | |
70 | return 0; | |
71 | dc = GetDC(NULL); | |
72 | oldFont = (HFONT)SelectObject(dc, font); | |
73 | ||
8457948a TC |
74 | { |
75 | char facename[100]; | |
76 | if (GetTextFace(dc, sizeof(facename), facename)) { | |
77 | mm_log((1, " face: %s\n", facename)); | |
78 | } | |
79 | } | |
80 | ||
bd4f550c | 81 | workp = text; |
50e800e9 | 82 | work_len = length; |
bd4f550c TC |
83 | while (work_len > 0) { |
84 | unsigned long c; | |
85 | unsigned char cp; | |
86 | ||
87 | if (utf8) { | |
88 | c = i_utf8_advance(&workp, &work_len); | |
89 | if (c == ~0UL) { | |
90 | i_push_error(0, "invalid UTF8 character"); | |
91 | return 0; | |
92 | } | |
93 | } | |
94 | else { | |
50e800e9 | 95 | c = (unsigned char)*workp++; |
bd4f550c TC |
96 | --work_len; |
97 | } | |
50e800e9 TC |
98 | if (!got_first_ch) { |
99 | first_ch = c; | |
100 | ++got_first_ch; | |
101 | } | |
102 | last_ch = c; | |
bd4f550c TC |
103 | |
104 | cp = c > '~' ? '.' : c < ' ' ? '.' : c; | |
a6d9b737 TC |
105 | |
106 | memset(&mat, 0, sizeof(mat)); | |
107 | mat.eM11.value = 1; | |
108 | mat.eM22.value = 1; | |
bd4f550c | 109 | if (GetGlyphOutline(dc, (UINT)c, GGO_METRICS, &gm, 0, NULL, &mat) != GDI_ERROR) { |
a6d9b737 TC |
110 | mm_log((2, " glyph '%c' (%02x): bbx (%u,%u) org (%d,%d) inc(%d,%d)\n", |
111 | cp, c, gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmptGlyphOrigin.x, | |
112 | gm.gmptGlyphOrigin.y, gm.gmCellIncX, gm.gmCellIncY)); | |
113 | ||
114 | ascent = gm.gmptGlyphOrigin.y; | |
115 | descent = ascent - gm.gmBlackBoxY; | |
116 | if (ascent > max_ascent) max_ascent = ascent; | |
117 | if (descent < min_descent) min_descent = descent; | |
118 | } | |
119 | else { | |
120 | mm_log((1, " glyph '%c' (%02x): error %d\n", cp, c, GetLastError())); | |
121 | } | |
122 | } | |
123 | ||
bd4f550c TC |
124 | if (utf8) { |
125 | int wide_chars; | |
126 | LPWSTR wide_text = utf8_to_wide_string(text, length, &wide_chars); | |
127 | ||
128 | if (!wide_text) | |
129 | return 0; | |
130 | ||
131 | if (!GetTextExtentPoint32W(dc, wide_text, wide_chars, &sz) | |
132 | || !GetTextMetrics(dc, &tm)) { | |
133 | SelectObject(dc, oldFont); | |
134 | ReleaseDC(NULL, dc); | |
135 | DeleteObject(font); | |
136 | return 0; | |
137 | } | |
138 | ||
139 | myfree(wide_text); | |
140 | } | |
141 | else { | |
142 | if (!GetTextExtentPoint32(dc, text, length, &sz) | |
143 | || !GetTextMetrics(dc, &tm)) { | |
144 | SelectObject(dc, oldFont); | |
145 | ReleaseDC(NULL, dc); | |
146 | DeleteObject(font); | |
147 | return 0; | |
148 | } | |
faa9b3e7 | 149 | } |
50e800e9 TC |
150 | bbox[BBOX_GLOBAL_DESCENT] = -tm.tmDescent; |
151 | bbox[BBOX_DESCENT] = min_descent == size ? -tm.tmDescent : min_descent; | |
8457948a TC |
152 | bbox[BBOX_POS_WIDTH] = sz.cx; |
153 | bbox[BBOX_ADVANCE_WIDTH] = sz.cx; | |
a6d9b737 TC |
154 | bbox[BBOX_GLOBAL_ASCENT] = tm.tmAscent; |
155 | bbox[BBOX_ASCENT] = max_ascent == -size ? tm.tmAscent : max_ascent; | |
faa9b3e7 | 156 | |
8457948a | 157 | if (length |
50e800e9 TC |
158 | && GetCharABCWidths(dc, first_ch, first_ch, &first) |
159 | && GetCharABCWidths(dc, last_ch, last_ch, &last)) { | |
160 | mm_log((1, "first: %d A: %d B: %d C: %d\n", first_ch, | |
8457948a | 161 | first.abcA, first.abcB, first.abcC)); |
50e800e9 | 162 | mm_log((1, "last: %d A: %d B: %d C: %d\n", last_ch, |
8457948a TC |
163 | last.abcA, last.abcB, last.abcC)); |
164 | bbox[BBOX_NEG_WIDTH] = first.abcA; | |
165 | bbox[BBOX_RIGHT_BEARING] = last.abcC; | |
faa9b3e7 | 166 | if (last.abcC < 0) |
8457948a | 167 | bbox[BBOX_POS_WIDTH] -= last.abcC; |
faa9b3e7 TC |
168 | } |
169 | else { | |
8457948a TC |
170 | bbox[BBOX_NEG_WIDTH] = 0; |
171 | bbox[BBOX_RIGHT_BEARING] = 0; | |
faa9b3e7 | 172 | } |
faa9b3e7 TC |
173 | |
174 | SelectObject(dc, oldFont); | |
175 | ReleaseDC(NULL, dc); | |
176 | DeleteObject(font); | |
177 | ||
8d14daab TC |
178 | mm_log((1, " bbox=> negw=%" i_DF " glob_desc=%" i_DF " pos_wid=%" i_DF |
179 | " glob_asc=%" i_DF " desc=%" i_DF " asc=%" i_DF " adv_width=%" i_DF | |
180 | " rightb=%" i_DF "\n", i_DFc(bbox[0]), i_DFc(bbox[1]), i_DFc(bbox[2]), | |
181 | i_DFc(bbox[3]), i_DFc(bbox[4]), i_DFc(bbox[5]), i_DFc(bbox[6]), | |
182 | i_DFc(bbox[7]))); | |
8457948a TC |
183 | |
184 | return BBOX_RIGHT_BEARING + 1; | |
faa9b3e7 TC |
185 | } |
186 | ||
c13a1ea8 TC |
187 | /* |
188 | =item i_wf_text(face, im, tx, ty, cl, size, text, len, align, aa) | |
189 | ||
190 | Draws the text in the given color. | |
191 | ||
192 | =cut | |
193 | */ | |
faa9b3e7 | 194 | |
2c68060c | 195 | int |
8d14daab TC |
196 | i_wf_text(const char *face, i_img *im, i_img_dim tx, i_img_dim ty, const i_color *cl, i_img_dim size, |
197 | const char *text, size_t len, int align, int aa, int utf8) { | |
2c68060c | 198 | unsigned char *bits; |
2c68060c | 199 | SIZE sz; |
5091168a | 200 | size_t line_width; |
8d14daab | 201 | i_img_dim x, y; |
2c68060c TC |
202 | int ch; |
203 | TEXTMETRIC tm; | |
204 | int top; | |
8d14daab | 205 | i_img_dim bbox[BOUNDING_BOX_COUNT]; |
5091168a TC |
206 | i_render *r; |
207 | unsigned char *outp; | |
8d14daab TC |
208 | |
209 | i_clear_error(); | |
50e800e9 | 210 | |
8d14daab | 211 | mm_log((1, "i_wf_text(face %s, im %p, tx %" i_DF ", ty %" i_DF ", cl %p, size %" i_DF ", text %p, length %lu, align %d, aa %d, utf8 %d)\n", face, im, i_DFcp(tx, ty), cl, i_DFc(size), text, (unsigned long)len, align, aa, aa, utf8)); |
2c68060c | 212 | |
50e800e9 TC |
213 | if (!i_wf_bbox(face, size, text, len, bbox, utf8)) |
214 | return 0; | |
215 | ||
5091168a | 216 | bits = render_text(face, size, text, len, aa, &sz, &tm, &line_width, bbox, utf8); |
2c68060c TC |
217 | if (!bits) |
218 | return 0; | |
5091168a | 219 | |
50e800e9 | 220 | tx += bbox[BBOX_NEG_WIDTH]; |
2c68060c | 221 | top = ty; |
a6d9b737 | 222 | if (align) { |
2c68060c | 223 | top -= tm.tmAscent; |
a6d9b737 TC |
224 | } |
225 | else { | |
a6d9b737 TC |
226 | top -= tm.tmAscent - bbox[BBOX_ASCENT]; |
227 | } | |
2c68060c | 228 | |
5091168a TC |
229 | r = i_render_new(im, sz.cx); |
230 | outp = bits; | |
2c68060c | 231 | for (y = 0; y < sz.cy; ++y) { |
5091168a TC |
232 | i_render_color(r, tx, top + sz.cy - y - 1, sz.cx, outp, cl); |
233 | outp += line_width; | |
2c68060c | 234 | } |
5091168a TC |
235 | i_render_delete(r); |
236 | myfree(bits); | |
2c68060c TC |
237 | |
238 | return 1; | |
239 | } | |
240 | ||
c13a1ea8 TC |
241 | /* |
242 | =item i_wf_cp(face, im, tx, ty, channel, size, text, len, align, aa) | |
243 | ||
244 | Draws the text in the given channel. | |
245 | ||
246 | =cut | |
247 | */ | |
248 | ||
2c68060c | 249 | int |
8d14daab TC |
250 | i_wf_cp(const char *face, i_img *im, i_img_dim tx, i_img_dim ty, int channel, i_img_dim size, |
251 | const char *text, size_t len, int align, int aa, int utf8) { | |
2c68060c | 252 | unsigned char *bits; |
2c68060c | 253 | SIZE sz; |
5091168a | 254 | size_t line_width; |
8d14daab | 255 | i_img_dim x, y; |
2c68060c | 256 | TEXTMETRIC tm; |
8d14daab TC |
257 | i_img_dim top; |
258 | i_img_dim bbox[BOUNDING_BOX_COUNT]; | |
5091168a | 259 | unsigned char *outp; |
8d14daab TC |
260 | |
261 | i_clear_error(); | |
2c68060c | 262 | |
8d14daab | 263 | mm_log((1, "i_wf_cp(face %s, im %p, tx %" i_DF ", ty %" i_DF ", channel %d, size %" i_DF ", text %p, length %lu, align %d, aa %d, utf8 %d)\n", face, im, i_DFcp(tx, ty), channel, i_DFc(size), text, (unsigned long)len, align, aa, aa, utf8)); |
50e800e9 TC |
264 | |
265 | if (!i_wf_bbox(face, size, text, len, bbox, utf8)) | |
266 | return 0; | |
267 | ||
5091168a | 268 | bits = render_text(face, size, text, len, aa, &sz, &tm, &line_width, bbox, utf8); |
2c68060c TC |
269 | if (!bits) |
270 | return 0; | |
271 | ||
2c68060c | 272 | top = ty; |
a6d9b737 | 273 | if (align) { |
2c68060c | 274 | top -= tm.tmAscent; |
a6d9b737 TC |
275 | } |
276 | else { | |
a6d9b737 TC |
277 | top -= tm.tmAscent - bbox[BBOX_ASCENT]; |
278 | } | |
2c68060c | 279 | |
5091168a | 280 | outp = bits; |
2c68060c TC |
281 | for (y = 0; y < sz.cy; ++y) { |
282 | for (x = 0; x < sz.cx; ++x) { | |
283 | i_color pel; | |
5091168a | 284 | int scale = outp[x]; |
2c68060c TC |
285 | i_gpix(im, tx+x, top+sz.cy-y-1, &pel); |
286 | pel.channel[channel] = scale; | |
287 | i_ppix(im, tx+x, top+sz.cy-y-1, &pel); | |
288 | } | |
5091168a | 289 | outp += line_width; |
2c68060c | 290 | } |
5091168a | 291 | myfree(bits); |
2c68060c TC |
292 | |
293 | return 1; | |
294 | } | |
295 | ||
8ceea84c TC |
296 | static HMODULE gdi_dll; |
297 | typedef BOOL (CALLBACK *AddFontResourceExA_t)(LPCSTR, DWORD, PVOID); | |
298 | static AddFontResourceExA_t AddFontResourceExAp; | |
299 | typedef BOOL (CALLBACK *RemoveFontResourceExA_t)(LPCSTR, DWORD, PVOID); | |
300 | static RemoveFontResourceExA_t RemoveFontResourceExAp; | |
301 | ||
8457948a TC |
302 | /* |
303 | =item i_wf_addfont(char const *filename, char const *resource_file) | |
304 | ||
305 | Adds a TTF font file as usable by the application. | |
306 | ||
8ceea84c TC |
307 | Where possible the font is added as private to the application. |
308 | ||
309 | Under Windows 95/98/ME the font is added globally, since we don't have | |
310 | any choice. In either case call i_wf_delfont() to remove it. | |
8457948a TC |
311 | |
312 | =cut | |
313 | */ | |
314 | int | |
315 | i_wf_addfont(char const *filename) { | |
316 | i_clear_error(); | |
317 | ||
b8b889ba | 318 | mm_log((1, "i_wf_addfont(%s)\n", filename)); |
8ceea84c TC |
319 | if (!gdi_dll) { |
320 | gdi_dll = GetModuleHandle("GDI32"); | |
321 | if (gdi_dll) { | |
322 | AddFontResourceExAp = (AddFontResourceExA_t)GetProcAddress(gdi_dll, "AddFontResourceExA"); | |
323 | RemoveFontResourceExAp = (RemoveFontResourceExA_t)GetProcAddress(gdi_dll, "RemoveFontResourceExA"); | |
b8b889ba TC |
324 | mm_log((1, "i_wf_addfont: AddFontResourceExA %p RemoveFontResourceExA %p\n", |
325 | AddFontResourceExAp, RemoveFontResourceExAp)); | |
8ceea84c TC |
326 | } |
327 | } | |
328 | ||
b8b889ba TC |
329 | if (AddFontResourceExAp && RemoveFontResourceExAp) { |
330 | mm_log((1, "i_wf_addfont: adding via AddFontResourceEx()\n")); | |
331 | if (AddFontResourceExAp(filename, FR_PRIVATE, 0)) { | |
332 | return 1; | |
333 | } | |
8457948a TC |
334 | } |
335 | else { | |
b8b889ba TC |
336 | mm_log((1, "i_wf_addfont: adding via AddFontResource()\n")); |
337 | if (AddFontResource(filename)) { | |
338 | return 1; | |
339 | } | |
8457948a | 340 | } |
b8b889ba TC |
341 | |
342 | mm_log((1, "i_wf_addfont failed: %ld\n", GetLastError())); | |
343 | i_push_errorf(0, "Could not add resource: %ld", GetLastError()); | |
344 | return 0; | |
8457948a TC |
345 | } |
346 | ||
8ceea84c TC |
347 | /* |
348 | =item i_wf_delfont(char const *filename, char const *resource_file) | |
349 | ||
350 | Deletes a TTF font file as usable by the application. | |
351 | ||
352 | =cut | |
353 | */ | |
354 | int | |
355 | i_wf_delfont(char const *filename) { | |
356 | i_clear_error(); | |
357 | ||
b8b889ba TC |
358 | mm_log((1, "i_wf_delfont(%s)\n", filename)); |
359 | ||
360 | if (AddFontResourceExAp && RemoveFontResourceExAp) { | |
361 | mm_log((1, "i_wf_delfont: removing via RemoveFontResourceEx()\n")); | |
362 | if (RemoveFontResourceExAp(filename, FR_PRIVATE, 0)) | |
363 | return 1; | |
8ceea84c TC |
364 | } |
365 | else { | |
b8b889ba TC |
366 | mm_log((1, "i_wf_delfont: adding via RemoveFontResourceEx()\n")); |
367 | if (RemoveFontResource(filename)) | |
368 | return 1; | |
8ceea84c | 369 | } |
b8b889ba TC |
370 | |
371 | mm_log((1, "i_wf_delfont failed: %ld\n", GetLastError())); | |
372 | i_push_errorf(0, "Could not remove resource: %ld", GetLastError()); | |
373 | return 0; | |
8ceea84c TC |
374 | } |
375 | ||
c13a1ea8 TC |
376 | /* |
377 | =back | |
378 | ||
379 | =head1 INTERNAL FUNCTIONS | |
380 | ||
381 | =over | |
382 | ||
383 | =item set_logfont(face, size, lf) | |
384 | ||
385 | Fills in a LOGFONT structure with reasonable defaults. | |
386 | ||
387 | =cut | |
388 | */ | |
389 | ||
885e13c5 | 390 | static void set_logfont(const char *face, int size, LOGFONT *lf) { |
2c68060c TC |
391 | memset(lf, 0, sizeof(LOGFONT)); |
392 | ||
393 | lf->lfHeight = -size; /* character height rather than cell height */ | |
ea9e6c3f | 394 | lf->lfCharSet = DEFAULT_CHARSET; |
2c68060c TC |
395 | lf->lfOutPrecision = OUT_TT_PRECIS; |
396 | lf->lfClipPrecision = CLIP_DEFAULT_PRECIS; | |
397 | lf->lfQuality = PROOF_QUALITY; | |
398 | strncpy(lf->lfFaceName, face, sizeof(lf->lfFaceName)-1); | |
399 | /* NUL terminated by the memset at the top */ | |
400 | } | |
401 | ||
c13a1ea8 TC |
402 | /* |
403 | =item render_text(face, size, text, length, aa, pbm, psz, tm) | |
404 | ||
405 | renders the text to an in-memory RGB bitmap | |
406 | ||
407 | It would be nice to render to greyscale, but Windows doesn't have | |
408 | native greyscale bitmaps. | |
409 | ||
410 | =cut | |
411 | */ | |
5091168a TC |
412 | static unsigned char * |
413 | render_text(const char *face, int size, const char *text, size_t length, int aa, | |
414 | SIZE *psz, TEXTMETRIC *tm, size_t *bytes_per_line, i_img_dim *bbox, int utf8) { | |
faa9b3e7 TC |
415 | BITMAPINFO bmi; |
416 | BITMAPINFOHEADER *bmih = &bmi.bmiHeader; | |
417 | HDC dc, bmpDc; | |
418 | LOGFONT lf; | |
419 | HFONT font, oldFont; | |
420 | SIZE sz; | |
421 | HBITMAP bm, oldBm; | |
422 | LPVOID bits; | |
bd4f550c TC |
423 | int wide_count; |
424 | LPWSTR wide_text; | |
5091168a | 425 | unsigned char *result; |
bd4f550c | 426 | |
faa9b3e7 TC |
427 | dc = GetDC(NULL); |
428 | set_logfont(face, size, &lf); | |
429 | ||
430 | #ifdef ANTIALIASED_QUALITY | |
431 | /* See KB article Q197076 | |
432 | "INFO: Controlling Anti-aliased Text via the LOGFONT Structure" | |
433 | */ | |
434 | lf.lfQuality = aa ? ANTIALIASED_QUALITY : NONANTIALIASED_QUALITY; | |
435 | #endif | |
436 | ||
bd4f550c TC |
437 | if (utf8) { |
438 | wide_text = utf8_to_wide_string(text, length, &wide_count); | |
439 | } | |
440 | else { | |
5091168a | 441 | wide_text = latin1_to_wide_string(text, length, &wide_count); |
bd4f550c TC |
442 | } |
443 | ||
faa9b3e7 TC |
444 | bmpDc = CreateCompatibleDC(dc); |
445 | if (bmpDc) { | |
446 | font = CreateFontIndirect(&lf); | |
447 | if (font) { | |
448 | oldFont = SelectObject(bmpDc, font); | |
50e800e9 | 449 | |
faa9b3e7 | 450 | GetTextMetrics(bmpDc, tm); |
50e800e9 TC |
451 | sz.cx = bbox[BBOX_ADVANCE_WIDTH] - bbox[BBOX_NEG_WIDTH] + bbox[BBOX_POS_WIDTH]; |
452 | sz.cy = bbox[BBOX_GLOBAL_ASCENT] - bbox[BBOX_GLOBAL_DESCENT]; | |
faa9b3e7 TC |
453 | |
454 | memset(&bmi, 0, sizeof(bmi)); | |
455 | bmih->biSize = sizeof(*bmih); | |
456 | bmih->biWidth = sz.cx; | |
457 | bmih->biHeight = sz.cy; | |
458 | bmih->biPlanes = 1; | |
459 | bmih->biBitCount = 24; | |
460 | bmih->biCompression = BI_RGB; | |
461 | bmih->biSizeImage = 0; | |
a6d9b737 | 462 | bmih->biXPelsPerMeter = (LONG)(72 / 2.54 * 100); |
faa9b3e7 TC |
463 | bmih->biYPelsPerMeter = bmih->biXPelsPerMeter; |
464 | bmih->biClrUsed = 0; | |
465 | bmih->biClrImportant = 0; | |
466 | ||
467 | bm = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0); | |
468 | ||
469 | if (bm) { | |
470 | oldBm = SelectObject(bmpDc, bm); | |
471 | SetTextColor(bmpDc, RGB(255, 255, 255)); | |
472 | SetBkColor(bmpDc, RGB(0, 0, 0)); | |
5091168a | 473 | TextOutW(bmpDc, -bbox[BBOX_NEG_WIDTH], 0, wide_text, wide_count); |
faa9b3e7 TC |
474 | SelectObject(bmpDc, oldBm); |
475 | } | |
476 | else { | |
477 | i_push_errorf(0, "Could not create DIB section for render: %ld", | |
478 | GetLastError()); | |
479 | SelectObject(bmpDc, oldFont); | |
480 | DeleteObject(font); | |
481 | DeleteDC(bmpDc); | |
482 | ReleaseDC(NULL, dc); | |
5091168a | 483 | myfree(wide_text); |
faa9b3e7 TC |
484 | return NULL; |
485 | } | |
486 | SelectObject(bmpDc, oldFont); | |
487 | DeleteObject(font); | |
488 | } | |
489 | else { | |
5091168a | 490 | myfree(wide_text); |
faa9b3e7 TC |
491 | i_push_errorf(0, "Could not create logical font: %ld", |
492 | GetLastError()); | |
493 | DeleteDC(bmpDc); | |
494 | ReleaseDC(NULL, dc); | |
495 | return NULL; | |
496 | } | |
497 | DeleteDC(bmpDc); | |
498 | } | |
499 | else { | |
5091168a | 500 | myfree(wide_text); |
faa9b3e7 TC |
501 | i_push_errorf(0, "Could not create rendering DC: %ld", GetLastError()); |
502 | ReleaseDC(NULL, dc); | |
503 | return NULL; | |
504 | } | |
505 | ||
5091168a | 506 | myfree(wide_text); |
bd4f550c | 507 | |
faa9b3e7 TC |
508 | ReleaseDC(NULL, dc); |
509 | ||
faa9b3e7 TC |
510 | *psz = sz; |
511 | ||
5091168a TC |
512 | /* convert into a map we can just pass to i_render_color() */ |
513 | { | |
514 | i_img_dim x, y; | |
515 | unsigned char *outp, *ucbits; | |
516 | size_t bits_line_width; | |
517 | ||
518 | *bytes_per_line = sz.cx; | |
519 | result = mymalloc(sz.cx * sz.cy); | |
520 | outp = result; | |
521 | ucbits = bits; | |
522 | bits_line_width = sz.cx * 3; | |
523 | bits_line_width = (bits_line_width + 3) / 4 * 4; | |
524 | ||
525 | for (y = 0; y < sz.cy; ++y) { | |
526 | for (x = 0; x < sz.cx; ++x) { | |
527 | *outp++ = ucbits[3 * x]; | |
528 | } | |
529 | ucbits += bits_line_width; | |
530 | } | |
531 | } | |
532 | DeleteObject(bm); | |
533 | ||
534 | return result; | |
faa9b3e7 TC |
535 | } |
536 | ||
c13a1ea8 | 537 | /* |
bd4f550c TC |
538 | =item utf8_to_wide_string(text, text_len, wide_chars) |
539 | ||
540 | =cut | |
541 | */ | |
542 | ||
543 | static | |
544 | LPWSTR | |
545 | utf8_to_wide_string(char const *text, int text_len, int *wide_chars) { | |
405c8105 | 546 | int wide_count = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, text_len, NULL, 0); |
bd4f550c TC |
547 | LPWSTR result; |
548 | ||
549 | if (wide_count < 0) { | |
550 | i_push_errorf(0, "Could not convert utf8: %ld", GetLastError()); | |
551 | return NULL; | |
552 | } | |
553 | ++wide_count; | |
554 | result = mymalloc(sizeof(WCHAR) * wide_count); | |
405c8105 | 555 | if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, text_len, result, wide_count) < 0) { |
bd4f550c TC |
556 | i_push_errorf(0, "Could not convert utf8: %ld", GetLastError()); |
557 | return NULL; | |
558 | } | |
559 | ||
560 | result[wide_count-1] = (WCHAR)'\0'; | |
561 | *wide_chars = wide_count - 1; | |
562 | ||
563 | return result; | |
564 | } | |
565 | ||
5091168a TC |
566 | static LPWSTR |
567 | latin1_to_wide_string(char const *text, int text_len, int *wide_chars) { | |
568 | LPWSTR result = mymalloc(sizeof(WCHAR) * (text_len + 1)); | |
569 | size_t i; | |
570 | ||
571 | for (i = 0; i < text_len; ++i) { | |
572 | result[i] = (unsigned char)text[i]; | |
573 | } | |
574 | result[i] = 0; | |
575 | *wide_chars = text_len; | |
576 | ||
577 | return result; | |
578 | } | |
bd4f550c TC |
579 | |
580 | /* | |
581 | =back | |
582 | ||
c13a1ea8 TC |
583 | =head1 BUGS |
584 | ||
585 | Should really use a structure so we can set more attributes. | |
586 | ||
587 | Should support UTF8 | |
588 | ||
589 | =head1 AUTHOR | |
590 | ||
591 | Tony Cook <tony@develop-help.com> | |
592 | ||
593 | =head1 SEE ALSO | |
594 | ||
595 | Imager(3) | |
596 | ||
597 | =cut | |
598 | */ |