FreeRDP
Loading...
Searching...
No Matches
rdtk_font.c
1
19#include <rdtk/config.h>
20
21#include <errno.h>
22
23#include <winpr/config.h>
24#include <winpr/wtypes.h>
25#include <winpr/crt.h>
26#include <winpr/assert.h>
27#include <winpr/cast.h>
28#include <winpr/path.h>
29#include <winpr/file.h>
30#include <winpr/print.h>
31
32#include "rdtk_engine.h"
33#include "rdtk_resources.h"
34#include "rdtk_surface.h"
35
36#include "rdtk_font.h"
37
38#if defined(WINPR_WITH_PNG)
39#define FILE_EXT "png"
40#else
41#define FILE_EXT "bmp"
42#endif
43
44static int rdtk_font_draw_glyph(rdtkSurface* surface, uint16_t nXDst, uint16_t nYDst,
45 rdtkFont* font, rdtkGlyph* glyph)
46{
47 WINPR_ASSERT(surface);
48 WINPR_ASSERT(font);
49 WINPR_ASSERT(glyph);
50
51 nXDst += glyph->offsetX;
52 nYDst += glyph->offsetY;
53 const size_t nXSrc = WINPR_ASSERTING_INT_CAST(size_t, glyph->rectX);
54 const size_t nYSrc = WINPR_ASSERTING_INT_CAST(size_t, glyph->rectY);
55 const size_t nWidth = WINPR_ASSERTING_INT_CAST(size_t, glyph->rectWidth);
56 const size_t nHeight = WINPR_ASSERTING_INT_CAST(size_t, glyph->rectHeight);
57 const uint32_t nSrcStep = font->image->scanline;
58 const uint8_t* pSrcData = font->image->data;
59 uint8_t* pDstData = surface->data;
60 const uint32_t nDstStep = surface->scanline;
61
62 for (size_t y = 0; y < nHeight; y++)
63 {
64 const uint8_t* pSrcPixel = &pSrcData[((1ULL * nYSrc + y) * nSrcStep) + (4ULL * nXSrc)];
65 uint8_t* pDstPixel = &pDstData[((1ULL * nYDst + y) * nDstStep) + (4ULL * nXDst)];
66
67 for (size_t x = 0; x < nWidth; x++)
68 {
69 uint8_t B = pSrcPixel[0];
70 uint8_t G = pSrcPixel[1];
71 uint8_t R = pSrcPixel[2];
72 uint8_t A = pSrcPixel[3];
73 pSrcPixel += 4;
74
75 if (1)
76 {
77 /* tint black */
78 R = 255 - R;
79 G = 255 - G;
80 B = 255 - B;
81 }
82
83 if (A == 255)
84 {
85 pDstPixel[0] = B;
86 pDstPixel[1] = G;
87 pDstPixel[2] = R;
88 }
89 else
90 {
91 R = (R * A) / 255;
92 G = (G * A) / 255;
93 B = (B * A) / 255;
94 pDstPixel[0] = B + (pDstPixel[0] * (255 - A) + (255 / 2)) / 255;
95 pDstPixel[1] = G + (pDstPixel[1] * (255 - A) + (255 / 2)) / 255;
96 pDstPixel[2] = R + (pDstPixel[2] * (255 - A) + (255 / 2)) / 255;
97 }
98
99 pDstPixel[3] = 0xFF;
100 pDstPixel += 4;
101 }
102 }
103
104 return 1;
105}
106
107int rdtk_font_draw_text(rdtkSurface* surface, uint16_t nXDst, uint16_t nYDst, rdtkFont* font,
108 const char* text)
109{
110 WINPR_ASSERT(surface);
111 WINPR_ASSERT(font);
112 WINPR_ASSERT(text);
113
114 const size_t length = strlen(text);
115 for (size_t index = 0; index < length; index++)
116 {
117 rdtkGlyph* glyph = &font->glyphs[text[index] - 32];
118 rdtk_font_draw_glyph(surface, nXDst, nYDst, font, glyph);
119 nXDst += (glyph->width + 1);
120 }
121
122 return 1;
123}
124
125int rdtk_font_text_draw_size(rdtkFont* font, uint16_t* width, uint16_t* height, const char* text)
126{
127 WINPR_ASSERT(font);
128 WINPR_ASSERT(width);
129 WINPR_ASSERT(height);
130 WINPR_ASSERT(text);
131
132 *width = 0;
133 *height = 0;
134 const size_t length = strlen(text);
135 for (size_t index = 0; index < length; index++)
136 {
137 const size_t glyphIndex = WINPR_ASSERTING_INT_CAST(size_t, text[index] - 32);
138
139 if (glyphIndex < font->glyphCount)
140 {
141 rdtkGlyph* glyph = &font->glyphs[glyphIndex];
142 *width += (glyph->width + 1);
143 }
144 }
145
146 *height = font->height + 2;
147 return 1;
148}
149
150WINPR_ATTR_MALLOC(free, 1)
151WINPR_ATTR_NODISCARD
152static char* rdtk_font_load_descriptor_file(const char* filename, size_t* pSize)
153{
154 WINPR_ASSERT(filename);
155 WINPR_ASSERT(pSize);
156
157 union
158 {
159 size_t s;
160 INT64 i64;
161 } fileSize;
162 FILE* fp = winpr_fopen(filename, "r");
163
164 if (!fp)
165 return NULL;
166
167 if (_fseeki64(fp, 0, SEEK_END) != 0)
168 goto fail;
169 fileSize.i64 = _ftelli64(fp);
170 if (_fseeki64(fp, 0, SEEK_SET) != 0)
171 goto fail;
172
173 if (fileSize.i64 < 1)
174 goto fail;
175
176 {
177 char* buffer = (char*)calloc(fileSize.s + 4, sizeof(char));
178 if (!buffer)
179 goto fail;
180
181 {
182 size_t readSize = fread(buffer, fileSize.s, 1, fp);
183 if (readSize == 0)
184 {
185 if (!ferror(fp))
186 readSize = fileSize.s;
187 }
188
189 (void)fclose(fp);
190
191 if (readSize < 1)
192 {
193 free(buffer);
194 return NULL;
195 }
196 }
197
198 buffer[fileSize.s] = '\0';
199 buffer[fileSize.s + 1] = '\0';
200 *pSize = fileSize.s;
201 return buffer;
202 }
203
204fail:
205 (void)fclose(fp);
206 return NULL;
207}
208
209static int rdtk_font_convert_descriptor_code_to_utf8(const char* str, uint8_t* utf8)
210{
211 WINPR_ASSERT(str);
212 WINPR_ASSERT(utf8);
213
214 const size_t len = strlen(str);
215 *((uint32_t*)utf8) = 0;
216
217 if (len < 1)
218 return 1;
219
220 if (len == 1)
221 {
222 if ((str[0] > 31) && (str[0] < 127))
223 {
224 utf8[0] = WINPR_ASSERTING_INT_CAST(uint8_t, str[0] & 0xFF);
225 }
226 }
227 else
228 {
229 if (str[0] == '&')
230 {
231 const char* acc = &str[1];
232
233 if (strcmp(acc, "quot;") == 0)
234 utf8[0] = '"';
235 else if (strcmp(acc, "amp;") == 0)
236 utf8[0] = '&';
237 else if (strcmp(acc, "lt;") == 0)
238 utf8[0] = '<';
239 else if (strcmp(acc, "gt;") == 0)
240 utf8[0] = '>';
241 }
242 }
243
244 return 1;
245}
246
247static int rdtk_font_parse_descriptor_buffer(rdtkFont* font, char* buffer,
248 WINPR_ATTR_UNUSED size_t size)
249{
250 int rc = -1;
251
252 WINPR_ASSERT(font);
253
254 const char xmlversion[] = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
255 const char xmlfont[] = "<Font ";
256
257 char* p = strstr(buffer, xmlversion);
258
259 if (!p)
260 goto fail;
261
262 p += sizeof(xmlversion) - 1;
263 p = strstr(p, xmlfont);
264
265 if (!p)
266 goto fail;
267
268 p += sizeof(xmlfont) - 1;
269
270 /* find closing font tag */
271 {
272 char* end = strstr(p, "</Font>");
273 if (!end)
274 goto fail;
275
276 /* parse font size */
277 p = strstr(p, "size=\"");
278
279 if (!p)
280 goto fail;
281
282 p += sizeof("size=\"") - 1;
283
284 {
285 char* q = strchr(p, '"');
286 if (!q)
287 goto fail;
288
289 *q = '\0';
290 errno = 0;
291 {
292 long val = strtol(p, NULL, 0);
293
294 if ((errno != 0) || (val == 0) || (val > UINT32_MAX))
295 goto fail;
296
297 font->size = (UINT32)val;
298 }
299 *q = '"';
300
301 if (font->size <= 0)
302 goto fail;
303
304 p = q + 1;
305 }
306 /* parse font family */
307 p = strstr(p, "family=\"");
308
309 if (!p)
310 goto fail;
311
312 p += sizeof("family=\"") - 1;
313 {
314 char* q = strchr(p, '"');
315
316 if (!q)
317 goto fail;
318
319 *q = '\0';
320 font->family = _strdup(p);
321 *q = '"';
322
323 if (!font->family)
324 goto fail;
325
326 p = q + 1;
327 }
328 /* parse font height */
329 p = strstr(p, "height=\"");
330
331 if (!p)
332 goto fail;
333
334 p += sizeof("height=\"") - 1;
335 {
336 char* q = strchr(p, '"');
337
338 if (!q)
339 goto fail;
340
341 *q = '\0';
342 errno = 0;
343 {
344 const unsigned long val = strtoul(p, NULL, 0);
345
346 if ((errno != 0) || (val > UINT16_MAX))
347 goto fail;
348
349 font->height = (uint16_t)val;
350 }
351 *q = '"';
352
353 if (font->height <= 0)
354 goto fail;
355
356 p = q + 1;
357 }
358
359 /* parse font style */
360 p = strstr(p, "style=\"");
361
362 if (!p)
363 goto fail;
364
365 p += sizeof("style=\"") - 1;
366 {
367 char* q = strchr(p, '"');
368
369 if (!q)
370 goto fail;
371
372 *q = '\0';
373 font->style = _strdup(p);
374 *q = '"';
375
376 if (!font->style)
377 goto fail;
378
379 p = q + 1;
380 }
381
382 // printf("size: %d family: %s height: %d style: %s\n",
383 // font->size, font->family, font->height, font->style);
384 {
385 char* beg = p;
386 size_t count = 0;
387
388 while (p < end)
389 {
390 p = strstr(p, "<Char ");
391
392 if (!p)
393 goto fail;
394
395 p += sizeof("<Char ") - 1;
396 char* r = strstr(p, "/>");
397
398 if (!r)
399 goto fail;
400
401 *r = '\0';
402 p = r + sizeof("/>");
403 *r = '/';
404 count++;
405 }
406
407 if (count > UINT16_MAX)
408 goto fail;
409
410 font->glyphCount = (uint16_t)count;
411 font->glyphs = NULL;
412
413 if (count > 0)
414 font->glyphs = (rdtkGlyph*)calloc(font->glyphCount, sizeof(rdtkGlyph));
415
416 if (!font->glyphs)
417 goto fail;
418
419 p = beg;
420 }
421
422 {
423 size_t index = 0;
424 while (p < end)
425 {
426 p = strstr(p, "<Char ");
427
428 if (!p)
429 goto fail;
430
431 p += sizeof("<Char ") - 1;
432 char* r = strstr(p, "/>");
433
434 if (!r)
435 goto fail;
436
437 *r = '\0';
438 /* start parsing glyph */
439 if (index >= font->glyphCount)
440 goto fail;
441
442 rdtkGlyph* glyph = &font->glyphs[index];
443 /* parse glyph width */
444 p = strstr(p, "width=\"");
445
446 if (!p)
447 goto fail;
448
449 p += sizeof("width=\"") - 1;
450 {
451 char* q = strchr(p, '"');
452
453 if (!q)
454 goto fail;
455
456 *q = '\0';
457 errno = 0;
458 {
459 long val = strtol(p, NULL, 0);
460
461 if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
462 goto fail;
463
464 glyph->width = (INT32)val;
465 }
466 *q = '"';
467
468 if (glyph->width < 0)
469 goto fail;
470
471 p = q + 1;
472 }
473
474 /* parse glyph offset x,y */
475 p = strstr(p, "offset=\"");
476
477 if (!p)
478 goto fail;
479
480 p += sizeof("offset=\"") - 1;
481
482 char* tok[4] = { 0 };
483 {
484 char* q = strchr(p, '"');
485
486 if (!q)
487 goto fail;
488
489 *q = '\0';
490 tok[0] = p;
491 p = strchr(tok[0] + 1, ' ');
492
493 if (!p)
494 goto fail;
495
496 *p = 0;
497 tok[1] = p + 1;
498 errno = 0;
499 {
500 long val = strtol(tok[0], NULL, 0);
501
502 if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
503 goto fail;
504
505 glyph->offsetX = (INT32)val;
506 }
507 {
508 long val = strtol(tok[1], NULL, 0);
509
510 if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
511 goto fail;
512
513 glyph->offsetY = (INT32)val;
514 }
515 *q = '"';
516 p = q + 1;
517 }
518 /* parse glyph rect x,y,w,h */
519 p = strstr(p, "rect=\"");
520
521 if (!p)
522 goto fail;
523
524 p += sizeof("rect=\"") - 1;
525 {
526 char* q = strchr(p, '"');
527
528 if (!q)
529 goto fail;
530
531 *q = '\0';
532
533 tok[0] = p;
534 p = strchr(tok[0] + 1, ' ');
535
536 if (!p)
537 goto fail;
538
539 *p = 0;
540 tok[1] = p + 1;
541 p = strchr(tok[1] + 1, ' ');
542
543 if (!p)
544 goto fail;
545
546 *p = 0;
547 tok[2] = p + 1;
548 p = strchr(tok[2] + 1, ' ');
549
550 if (!p)
551 goto fail;
552
553 *p = 0;
554 tok[3] = p + 1;
555 errno = 0;
556 {
557 long val = strtol(tok[0], NULL, 0);
558
559 if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
560 goto fail;
561
562 glyph->rectX = (INT32)val;
563 }
564 {
565 long val = strtol(tok[1], NULL, 0);
566
567 if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
568 goto fail;
569
570 glyph->rectY = (INT32)val;
571 }
572 {
573 long val = strtol(tok[2], NULL, 0);
574
575 if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
576 goto fail;
577
578 glyph->rectWidth = (INT32)val;
579 }
580 {
581 long val = strtol(tok[3], NULL, 0);
582
583 if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
584 goto fail;
585
586 glyph->rectHeight = (INT32)val;
587 }
588 *q = '"';
589 p = q + 1;
590 }
591 /* parse code */
592 p = strstr(p, "code=\"");
593
594 if (!p)
595 goto fail;
596
597 p += sizeof("code=\"") - 1;
598 {
599 char* q = strchr(p, '"');
600
601 if (!q)
602 goto fail;
603
604 *q = '\0';
605 rdtk_font_convert_descriptor_code_to_utf8(p, glyph->code);
606 *q = '"';
607 }
608 /* finish parsing glyph */
609 p = r + sizeof("/>");
610 *r = '/';
611 index++;
612 }
613 }
614 }
615
616 rc = 1;
617
618fail:
619 free(buffer);
620 return rc;
621}
622
623static int rdtk_font_load_descriptor(rdtkFont* font, const char* filename)
624{
625 size_t size = 0;
626
627 WINPR_ASSERT(font);
628 char* buffer = rdtk_font_load_descriptor_file(filename, &size);
629
630 if (!buffer)
631 return -1;
632
633 return rdtk_font_parse_descriptor_buffer(font, buffer, size);
634}
635
636rdtkFont* rdtk_font_new(rdtkEngine* engine, const char* path, const char* file)
637{
638 size_t length = 0;
639 rdtkFont* font = NULL;
640 char* fontImageFile = NULL;
641 char* fontDescriptorFile = NULL;
642
643 WINPR_ASSERT(engine);
644 WINPR_ASSERT(path);
645 WINPR_ASSERT(file);
646
647 char* fontBaseFile = GetCombinedPath(path, file);
648 if (!fontBaseFile)
649 goto cleanup;
650
651 winpr_asprintf(&fontImageFile, &length, "%s." FILE_EXT, fontBaseFile);
652 if (!fontImageFile)
653 goto cleanup;
654
655 winpr_asprintf(&fontDescriptorFile, &length, "%s.xml", fontBaseFile);
656 if (!fontDescriptorFile)
657 goto cleanup;
658
659 if (!winpr_PathFileExists(fontImageFile))
660 goto cleanup;
661
662 if (!winpr_PathFileExists(fontDescriptorFile))
663 goto cleanup;
664
665 font = (rdtkFont*)calloc(1, sizeof(rdtkFont));
666
667 if (!font)
668 goto cleanup;
669
670 font->engine = engine;
671 font->image = winpr_image_new();
672
673 if (!font->image)
674 goto cleanup;
675
676 {
677 const int status = winpr_image_read(font->image, fontImageFile);
678 if (status < 0)
679 goto cleanup;
680 }
681
682 {
683 const int status2 = rdtk_font_load_descriptor(font, fontDescriptorFile);
684 if (status2 < 0)
685 goto cleanup;
686 }
687
688 free(fontBaseFile);
689 free(fontImageFile);
690 free(fontDescriptorFile);
691 return font;
692cleanup:
693 free(fontBaseFile);
694 free(fontImageFile);
695 free(fontDescriptorFile);
696
697 rdtk_font_free(font);
698 return NULL;
699}
700
701static rdtkFont* rdtk_embedded_font_new(rdtkEngine* engine, const uint8_t* imageData,
702 size_t imageSize, const uint8_t* descriptorData,
703 size_t descriptorSize)
704{
705 size_t size = 0;
706
707 WINPR_ASSERT(engine);
708
709 rdtkFont* font = (rdtkFont*)calloc(1, sizeof(rdtkFont));
710
711 if (!font)
712 return NULL;
713
714 font->engine = engine;
715 font->image = winpr_image_new();
716
717 if (!font->image)
718 {
719 free(font);
720 return NULL;
721 }
722
723 const int status = winpr_image_read_buffer(font->image, imageData, imageSize);
724 if (status < 0)
725 {
726 winpr_image_free(font->image, TRUE);
727 free(font);
728 return NULL;
729 }
730
731 size = descriptorSize;
732 char* buffer = (char*)calloc(size + 4, sizeof(char));
733
734 if (!buffer)
735 goto fail;
736
737 CopyMemory(buffer, descriptorData, size);
738 {
739 const int status2 = rdtk_font_parse_descriptor_buffer(font, buffer, size);
740 if (status2 < 0)
741 goto fail;
742 }
743
744 return font;
745
746fail:
747 rdtk_font_free(font);
748 return NULL;
749}
750
751void rdtk_font_free(rdtkFont* font)
752{
753 if (font)
754 {
755 free(font->family);
756 free(font->style);
757 winpr_image_free(font->image, TRUE);
758 free(font->glyphs);
759 free(font);
760 }
761}
762int rdtk_font_engine_init(rdtkEngine* engine)
763{
764 WINPR_ASSERT(engine);
765 if (!engine->font)
766 {
767 const uint8_t* imageData = NULL;
768 const uint8_t* descriptorData = NULL;
769 const SSIZE_T imageSize =
770 rdtk_get_embedded_resource_file("source_serif_pro_regular_12." FILE_EXT, &imageData);
771 const SSIZE_T descriptorSize =
772 rdtk_get_embedded_resource_file("source_serif_pro_regular_12.xml", &descriptorData);
773
774 if ((imageSize < 0) || (descriptorSize < 0))
775 return -1;
776
777 engine->font = rdtk_embedded_font_new(engine, imageData, (size_t)imageSize, descriptorData,
778 (size_t)descriptorSize);
779 if (!engine->font)
780 return -1;
781 }
782
783 return 1;
784}
785
786int rdtk_font_engine_uninit(rdtkEngine* engine)
787{
788 WINPR_ASSERT(engine);
789 if (engine->font)
790 {
791 rdtk_font_free(engine->font);
792 engine->font = NULL;
793 }
794
795 return 1;
796}